/*****************************************************************************
*    Copyright (C)2004 Ali Corporation. All Rights Reserved.
*
*    File:    This file contains S3501 DVBS2 basic function in LLD.
*
*    Description:    Header file in LLD.
*    History:
*     Date            Athor       Version   Reason
*     ============    =========   =======   =================
*     1.  05/18/2006  Berg        Ver 0.1   Create file for S3501 DVBS2 project
*     2.  06/30/2007  Dietel                Add synchronous mechanism support
*     3.  07/03/2007  Dietel                Add autoscan support ,Clean external macro
*     4.  07/03/2007  Dietel                Not using VBV_BUFFER

*     5.  03/18/2008  Carcy Liu             Open EQ, only mask EQ when 1/4, 1/3, 2/5 code rate.
                                              -> key word is "EQ_ENABLE".

*     6.  03/18/2008  Carcy Liu             Add description about I2C through.  Note:
                                            M3501 support I2C through function, but it should be
                                            careful when open & close I2C through function, otherwise
                                            the I2C slave may be "deadlock". Please do it as bellow.
                                                1, open I2C through function.
                                                2, wait 50ms at least.
                                                3, configure tuner
                                                4, wait 50ms at least.
                                                5, close I2C through function.
                                                6, wait 50ms at least.
                                               -> key word is "I2C_THROUGH_EN"

*     7.  03/21/2008  Carcy Liu             Disable HBCD disable function, HBCD is DVBS2 H8PSK
                                            option mode, if enable HBCD, firmware will spend
                                            more time to check HBCD when channel change.
                                            Suggest that disable HBCD.
                                                -> key word is "HBCD_DISABLE"
*     8.  04/04/2008  Carcy Liu             Update power control
                                                -> key word is "power control"

*     9.  04/18/2008  Carcy Liu             Update code rate information.

*     10. 04/21/2008  Carcy Liu             Add configure ZL10037's base band gain "G_BB_GAIN"

*     11. 04/23/2008  Carcy Liu             Software MUST send a reset pulse to the M3501 reset pin
                                            before it perform a channel change. This is because a bug
                                            is found in M3501 power saving logic which may cause
                                            abnormal large power consumption if it is not reset before
                                            a channel change is performed. It has been proven that
                                            after reset this bug is cleared.
                                            Key word "m3501_reset"

*     12.  04/28/2008  Carcy Liu            Show how to get LDPC code and other information:
                                            (All information show in the function)
                                            nim_s3501_reg_get_code_rate(UINT8* code_rate)
                                            nim_s3501_reg_get_map_type(UINT8*map_type)
                                            nim_s3501_reg_get_work_mode(UINT8*work_mode)
                                            nim_s3501_reg_get_roll_off(UINT8* roll_off)
                                            1, use nim_s3501_reg_get_work_mode to get work mode: dvbs or dvbs2
                                                    //  Work Mode
                                                    //      0x0:    DVB-S
                                                    //      0x1:    DVB-S2
                                                    //      0x2:    DVB-S2 HBC
                                            2, use nim_s3501_reg_get_map_type to get modulate type: QPSK/8SPK/16APSK
                                                    //      Map type:
                                                    //      0x0:    HBCD.
                                                    //      0x1:    BPSK
                                                    //      0x2:    QPSK
                                                    //      0x3:    8PSK
                                                    //      0x4:    16APSK
                                                    //      0x5:    32APSK
                                            3, use nim_s3501_reg_get_code_rate to get LDPC code rate.
                                                    //  Code rate list
                                                    //  for DVBS:
                                                    //      0x0:    1/2,
                                                    //      0x1:    2/3,
                                                    //      0x2:    3/4,
                                                    //      0x3:    5/6,
                                                    //      0x4:    6/7,
                                                    //      0x5:    7/8.
                                                    //  For DVBS2 :
                                                    //      0x0:    1/4 ,
                                                    //      0x1:    1/3 ,
                                                    //      0x2:    2/5 ,
                                                    //      0x3:    1/2 ,
                                                    //      0x4:    3/5 ,
                                                    //      0x5:    2/3 ,
                                                    //      0x6:    3/4 ,
                                                    //      0x7:    4/5 ,
                                                    //      0x8:    5/6 ,
                                                    //      0x9:    8/9 ,
                                                    //      0xa:    9/10.
                                            Key word "ldpc_code"

*     13. 04/28/2008  Carcy Liu             Add "nim_s3501_hw_init()" in m3501_reset().
                                            Make sure hardware reset successfully.
                                            Key word "nim_s3501_hw_init"

*     14. 2008-5-27   Carcy Liu         a.  Add dynamic power control: nim_s3501_dynamic_power()
                                            This function should be called every some seconds. In fact,
                                            MPEG host chip should get signal quality and intensity every
                                            some second, You can add nim_s3501_dynamic_power to it's tail.
                                            Every time when MPEG host chip get signal quality, it monitor
                                            and control M3501's power at the same time.

                                        b.  Add clock control in channel change, if channel lock,
                                            readback workmode, if workmode is DVBS, then slow down DVBS2
                                            clock to save power.

                                        c.  Add description for get signal quality and get signal intensity
                                            c1: get signal quality : nim_s3501_get_SNR
                                            c2: get signal intensity: nim_s3501_get_AGC

                                        d:  nim_s3501_get_BER and nim_s3501_get_PER.
                                            For DVBS2, BER is from BCH, it's meaning is very different with
                                            from viterbi in DVBS. So we use packet error rate (PER) for
                                            nim_s3501_get_BER when work in DVBS2.

                                            Key word : "power_ctrl"

*   15. 2008-12-2  Douglass Yan a. Delete some local static variable
                            b. Change attach function name
                            c. add variable to private structure to save qpsk addr
                            d. Update local function parameters num

*   16.2008-12-11 Douglass Yan  a. Delete all shared variable for support Dual S3501

*   17.2008-12-16 Douglass Yan  a. Delete maco REVERT_POLAR
*   18.2011-01-13 Seiya.Cao add annotation about DMX Bypass Mem Management:

      Fun:nim_s3501_fft_result_read(struct nim_device *dev);

      data = 0x3c;
      nim_reg_write(dev, RBD_CAP_PRM + 0x01, &data, 1); // set 220 packet

      Reason: for DMX I/Q bypass buffer was set 128k byte.so it can store

                          (128*1024)/188 = 697
              packets.a bit greater than seting 662 packets of course.

              Now that our buffer only can stor 697 packets,so RBD_CAP_PRM
              + 01 can only only smaller than this,but also than seeting 662
              packets,so that is why we setting 0x3f:

              Refer M3501 spec  about reg (RBD_CAP_PRM + 0x01),bit[0:5],one
              Unit is 1024 pair of I/Q samples,that mean one unit is

                          (1024*2)/188 = 10.9 packets.
              so 0x6f units means 10.9*60 = 653.6 packets.
              For 653.6 < 662 < 697,So Bypass memory is enough for use.

*****************************************************************************/
#include <api/libsi/si_tdt.h>
#include "nim_s3501.h"



#define DISEQC_DELAY    60   //adjust timing for DiSEqC circuit
#ifdef NIM_S3501_ASCAN_TOOLS
#include <api/libpub/as_module.h>
#define FREQ_P_V        5750
#define FREQ_P_H        5150
#define KU_C_BAND_INTERFACE 8000

struct nim_s3501_tp va_g2_ascan_tps[VD_REF_SATELLATE_CNT][VD_TP_CNT]=
{
#if 0
    //polarize: 0->H  1->V
    {
        {4171,FREQ_P_H-4171,    9200,   0,0},//979
        {4158,FREQ_P_H-4158,    8680,   0,0},//992
        {4147,FREQ_P_H-4147,    6150,   0,0},//1003
        {4115,FREQ_P_H-4115,    21375,  0,0},//1035
        {4080,FREQ_P_H-4080,    27500,  0,0},//1070
        {4040,FREQ_P_H-4040,    27500,  0,0},//1110
        {4000,FREQ_P_H-4000,    27500,  0,0},//1150
        {3972,FREQ_P_H-3972,    10000,  0,0},//1178
        {3960,FREQ_P_H-3960,    3570,   0,0},//1190
        {3950,FREQ_P_H-3950,    11400,  0,0},//1200
        {3920,FREQ_P_H-3920,    27500,  0,0},//1230
        {3880,FREQ_P_H-3880,    27500,  0,0},//1270
        {3840,FREQ_P_H-3840,    27500,  0,0},//1310
        {3818,FREQ_P_H-3818,    3160,   0,0},//1332
        {3808,FREQ_P_H-3808,    8800,   0,0},//1342
        {3796,FREQ_P_H-3796,    6930,   0,0},//1354
        {3786,FREQ_P_H-3786,    5440,   0,0},//1364
        {3769,FREQ_P_H-3769,    13400,  0,0},//1381
        {3750,FREQ_P_H-3750,    10490,  0,0},//1400
        {3730,FREQ_P_H-3730,    10720,  0,0},//1420
        {3706,FREQ_P_H-3706,    4420,   0,0},//1444
//      {3555,FREQ_P_H-3555,    1800,   0,0},//1595

        {4192,FREQ_P_V-4192,    6000,   1,0},//1558
        {4175,FREQ_P_V-4175,    18000,  1,0},//1575
        {4140,FREQ_P_V-4140,    27500,  1,0},//1610
        {4100,FREQ_P_V-4100,    27500,  1,0},//1650
        {4060,FREQ_P_V-4060,    27500,  1,0},//1690
        {4020,FREQ_P_V-4020,    27500,  1,0},//1730
        {3980,FREQ_P_V-3980,    27500,  1,0},//1770
        {3951,FREQ_P_V-3951,    9520,   1,0},//1799
        {3940,FREQ_P_V-3940,    5950,   1,0},//1810
        {3929,FREQ_P_V-3929,    8840,   1,0},//1821
        {3913,FREQ_P_V-3913,    6400,   1,0},//1837
        {3902,FREQ_P_V-3902,    9300,   1,0},//1848
        {3892,FREQ_P_V-3892,    4420,   1,0},//1858
        {3885,FREQ_P_V-3885,    4340,   1,0},//1865
        {3871,FREQ_P_V-3871,    9080,   1,0},//1879
        {3861,FREQ_P_V-3861,    4800,   1,0},//1889
        {3854,FREQ_P_V-3854,    4420,   1,0},//1896
        {3846,FREQ_P_V-3846,    5950,   1,0},//1904
        {3834,FREQ_P_V-3834,    5400,   1,0},//1916
        {3825,FREQ_P_V-3825,    6780,   1,0},//1925
        {3807,FREQ_P_V-3807,    6000,   1,0},//1943
        {3780,FREQ_P_V-3780,    27500,  1,0},//1970
        {3740,FREQ_P_V-3740,    27500,  1,0},//2010
        {3680,FREQ_P_V-3680,    27500,  1,0},//2070
        {3640,FREQ_P_V-3640,    27500,  1,0},//2110
        {3600,FREQ_P_V-3600,    27500,  1,0},//2150
                                        },
            //亚太V号
    {
       //H
        {12282,12282-10600,16300,  0,0},//1682
        {12316,12316-10600,1500,  0,0},//1716
        {12320,12320-10600,5000,  0,0},//1720
        {12334,12334-10600,7666, 0,0},//1734
        {12362,12362-10600,2220, 0,0},//1762
        {12380,12380-10600,2400,    0,0},//1780
        {12401,12401-10600,17575,   0,0},//1801
        {12429,12429-10600,3330, 0,0},//1829
        {12442,12442-10600,1537, 0,0},//1842
        {12630,12630-10600,43200,   0,0},//2030
        {12690,12690-10600,43200,   0,0},//2090
            //V
        {12275,12275-10600,15000, 1,0},//1675
        {12302,12302-10600,30000,   1,0},//1702
        {12354,12354-10600,43000,   1,0},//1754
        {12401,12401-10600,22425,   1,0},//1801
        {12430,12430-10600,22425,   1,0},//1830
        {12472,12472-10600,33500,   1,0},//1872
        {12537,12537-10600,41248,   1,0},//1937
        {12598,12598-10600,43000,   1,0},//1998
        {12646,12646-10600,22425,   1,0},//2046
        {12663,12663-10600,4285,    1,0},//2063
        {12669,12669-10600,4285,    1,0},//2069
        {12675,12675-10600,4285,    1,0},//2075
        {12685,12685-10600,3000,    1,0},//2085
        {12721,12721-10600,43000,   1,0},//2121
                                        },
            //新天6号
    {
        {11640,11640-9750,27500,  0,0},//1890
    {11676,11676-9750,27500,  0,0},//1926
    {12647,12647-10750,28000, 0,0},//1897
    {12686,12686-10750,28000, 0,0},//1936
        {12690,12690-10750,43200,  0,0},//1940
        {12728,12728-10750,26400,   0,0},//1978
       //
    {11130,11130-9750,30000,    1,0},//1380
    {11170,11170-9750,30000,    1,0},//1420
    {11540,11540-9750,43200,    1,0},//1790
    {11675,11675-9750,28800,    1,0},//1925
               },

        //马星3号
    {
        {4163,5150-4163,14400, 0,0},//987
        {4148,5150-4148,7200, 0,0},//1002
        {4120,5150-4120,30000, 0,0},//1030
        {4040,5150-4040,28600, 0,0},//1110
        {4000,5150-4000,30000, 0,0},//1150
        {3736,5150-3736,3000, 0,0},//1414
        {3710,5150-3710,2856, 0,0},//1440
      {3705,5150-3705,4290, 0,0},//1445

        {4120,5150-4120,29720, 1,0},//1030
        {4084,5150-4084,10000, 1,0},//1066
        {4035,5150-4035,3300, 1,0},//1115
        {4000,5150-4000,5000, 1,0},//1150
        {3920,5150-3920,29720, 1,0},//1230
        {3896,5150-3896,39000, 1,0},//1254
        {3887,5150-3887,3984, 1,0},//1263
        {3876,5150-3876,18385, 1,0},//1274
        {3864,5150-3864,2917, 1,0},//1286
        {3840,5150-3840,29720, 1,0},//1310
        {3814,5150-3814,6666, 1,0},//1336
        {3806,5150-3806,3255, 1,0},//1344
        {3794,5150-3794,5063, 1,0},//1356
        {3760,5150-3760,29720, 1,0},//1390
        {3721,5150-3721,2170, 1,0},//1429
        {3641,5150-3641,13333, 1,0},//1509
        {3616,FREQ_P_V-3616,3213, 1,0},//1534
                                     },
                  //越星1号
    {
        {10968,10968-9750,28800, 0,0},//1218
        {11008,11008-9750,28800, 0,0},//1258
        {11048,11048-9750,28800, 0,0},//1298
        {11087,11087-9750,28125, 0,0},//1337
        {11121,11121-9750,13333, 0,0},//1371
        {11135,11135-9750,9600, 0,0},//1385
        {11472,11472-9750,23200, 0,0},//1722
        {11517,11517-9750,4700, 0,0},//1767
        {11531,11531-9750,2500, 0,0},//1781
        {11549,11549-9750,28490, 0,0},//1799
        {11590,11590-9750,28800, 0,0},//1840
        {11629,11629-9750,28800, 0,0},//1879
        {11669,11669-9750,30000, 0,0},//1919
                                        },
                //亚太2R
    {
            //H
        {4175,5150-4175,4285, 0,0},//975
        {4169,5150-4169,4285, 0,0},//981
        {4163,5150-4163,4340, 0,0},//987
        {4145,5150-4145,3600, 0,0},//1005
        {4133,5150-4133,3750, 0,0},//1017
        {4119,5150-4119,4444, 0,0},//1031
        {4113,5150-4113,3695, 0,0},//1037
        {4109,5150-4109,2000, 0,0},//1041
        {4105,5150-4105,3125, 0,0},//1045
        {4085,5150-4085,2530, 0,0},//1065
        {4070,5150-4070,13020, 0,0},//1080
        {4056,5150-4056,3212, 0,0},//1094
        {4049,5150-4049,4290, 0,0},//1101
        {4034,5150-4034,4300, 0,0},//1116
        {4016,5150-4016,4340, 0,0},//1134
        {4009,5150-4009,4290, 0,0},//1141
        {4004,5150-4004,4324, 0,0},//1146
        {3997,5150-3997,3190, 0,0},//1153
        {3985,5150-3985,3700, 0,0},//1165
        {3960,5150-3960,30000, 0,0},//1190
        {3920,5150-3920,28340, 0,0},//1230
        {3880,5150-3880,28125, 0,0},//1270
        {3840,5150-3840,27500, 0,0},//1310
        {3815,5150-3815,3333, 0,0},//1335
        {3811,5150-3811,3980, 0,0},//1339
        {3805,5150-3805,4340, 0,0},//1345
        {3799,5150-3799,4330, 0,0},//1351
        {3793,5150-3793,4444, 0,0},//1357
        {3787,5150-3787,4600, 0,0},//1363
        {3720,5150-3720,29620, 0,0},//1430
        {3680,5150-3680,27500, 0,0},//1470
        {3655,5150-3655,3214, 0,0},//1495
        {3648,5150-3648,6500, 0,0},//1502
        {3629,5150-3629,1489, 0,0},//1521
            //V
        {4030,5150-4030,6312, 1,0},//1120
        {3993,5150-3993,1482, 1,0},//1157
        {3956,5150-3956,2340, 1,0},//1194
        {3814,5150-3814,6666, 1,0},//1336
        {3755,5150-3755,4640, 1,0},//1395
        {3747,5150-3747,8000, 1,0},//1403
        {3705,5150-3705,8888, 1,0},//1445
                                     },
                //亚洲3S
    {
        {4177,5150-4177,    2444,   0,0},//973
        {4172,5150-4172,    2480,   0,0},//978
        {4165,5150-4165,    5710,   0,0},//985
        {4160,5150-4160,    3030,   0,0},//990
        {4154,5150-4154,    5632,   0,0},//996
        {4146,5150-4146,    3784,   0,0},//1004
        {4132,5150-4132,    9200,   0,0},//1018
        {4125,5150-4125,    2444,   0,0},//1025
        {4097,5150-4097,    2256,   0,0},//1053
        {4071,5150-4071,    14240,  0,0},//1079
        {4040,5150-4040,    26500,  0,0},//1110
        {4000,5150-4000,    26850,  0,0},//1150
        {3960,5150-3960,    27500,  0,0},//1190
        {3920,5150-3920,    26666,  0,0},//1230
        {3880,5150-3880,    27500,  0,0},//1270
        {3840,5150-3840,    26850,  0,0},//1310
        {3760,5150-3760,    26000,  0,0},//1390
        {3729,5150-3729,    13650,  0,0},//1421
        {3706,5150-3706,    6000,   0,0},//1444
        {4180,5150-4180,    26666,  1,0},//970
        {4140,5150-4140,    27500,  1,0},//1010
        {4114,5150-4114,    7000,   1,0},//1036
        {4107,5150-4107,    2893,   1,0},//1043
        {4102,5150-4102,    2893,   1,0},//1048
        {4091,5150-4091,    13333,  1,0},//1059
        {4060,5150-4060,    26667,  1,0},//1090
        {4020,5150-4020,    27250,  1,0},//1130
        {3980,5150-3980,    28100,  1,0},//1170
        {3940,5150-3940,    28100,  1,0},//1210
        {3914,5150-3914,    7230,   1,0},//1236
        {3906,5150-3906,    2813,   1,0},//1244
        {3888,5150-3888,    9220,   1,0},//1262
        {3860,5150-3860,    27500,  1,0},//1290
        {3820,5150-3820,    27500,  1,0},//1330
        {3780,5150-3780,    28100,  1,0},//1395
        {3755,5150-3755,    4418,   1,0},//1395
        {3750,5150-3750,    3030,   1,0},//1400
        {3745,5150-3745,    2626,   1,0},//1405
        {3739,5150-3739,    2816,   1,0},//1411
        {3732,5150-3732,    6500,   1,0},//1418
        {3711,5150-3711,    11400,  1,0},//1439
        {3665,5150-3665,    7500,   1,0},//1485
                                        },
#else
    //polarize: 0->H  1->V
    {
        {FREQ_P_H,4171,FREQ_P_H-4171,   9200,   0,0},//979
        {FREQ_P_H,4158,FREQ_P_H-4158,   8680,   0,0},//992
        {FREQ_P_H,4147,FREQ_P_H-4147,   6150,   0,0},//1003
        {FREQ_P_H,4115,FREQ_P_H-4115,   21375,  0,0},//1035
        {FREQ_P_H,4080,FREQ_P_H-4080,   27500,  0,0},//1070
        {FREQ_P_H,4040,FREQ_P_H-4040,   27500,  0,0},//1110
        {FREQ_P_H,4000,FREQ_P_H-4000,   27500,  0,0},//1150
        {FREQ_P_H,3972,FREQ_P_H-3972,   10000,  0,0},//1178
        {FREQ_P_H,3960,FREQ_P_H-3960,   3570,   0,0},//1190
        {FREQ_P_H,3950,FREQ_P_H-3950,   11400,  0,0},//1200
        {FREQ_P_H,3920,FREQ_P_H-3920,   27500,  0,0},//1230
        {FREQ_P_H,3880,FREQ_P_H-3880,   27500,  0,0},//1270
        {FREQ_P_H,3840,FREQ_P_H-3840,   27500,  0,0},//1310
        {FREQ_P_H,3818,FREQ_P_H-3818,   3160,   0,0},//1332
        {FREQ_P_H,3808,FREQ_P_H-3808,   8800,   0,0},//1342
        {FREQ_P_H,3796,FREQ_P_H-3796,   6930,   0,0},//1354
        {FREQ_P_H,3786,FREQ_P_H-3786,   5440,   0,0},//1364
        {FREQ_P_H,3769,FREQ_P_H-3769,   13400,  0,0},//1381
        {FREQ_P_H,3750,FREQ_P_H-3750,   10490,  0,0},//1400
        {FREQ_P_H,3730,FREQ_P_H-3730,   10720,  0,0},//1420
        {FREQ_P_H,3706,FREQ_P_H-3706,   4420,   0,0},//1444
//      {3555,FREQ_P_H-3555,    1800,   0,0},//1595

        {FREQ_P_V,4192,FREQ_P_V-4192,   6000,   1,0},//1558
        {FREQ_P_V,4175,FREQ_P_V-4175,   18000,  1,0},//1575
        {FREQ_P_V,4140,FREQ_P_V-4140,   27500,  1,0},//1610
        {FREQ_P_V,4100,FREQ_P_V-4100,   27500,  1,0},//1650
        {FREQ_P_V,4060,FREQ_P_V-4060,   27500,  1,0},//1690
        {FREQ_P_V,4020,FREQ_P_V-4020,   27500,  1,0},//1730
        {FREQ_P_V,3980,FREQ_P_V-3980,   27500,  1,0},//1770
        {FREQ_P_V,3951,FREQ_P_V-3951,   9520,   1,0},//1799
        {FREQ_P_V,3940,FREQ_P_V-3940,   5950,   1,0},//1810
        {FREQ_P_V,3929,FREQ_P_V-3929,   8840,   1,0},//1821
        {FREQ_P_V,3913,FREQ_P_V-3913,   6400,   1,0},//1837
        {FREQ_P_V,3902,FREQ_P_V-3902,   9300,   1,0},//1848
        {FREQ_P_V,3892,FREQ_P_V-3892,   4420,   1,0},//1858
        {FREQ_P_V,3885,FREQ_P_V-3885,   4340,   1,0},//1865
        {FREQ_P_V,3871,FREQ_P_V-3871,   9080,   1,0},//1879
        {FREQ_P_V,3861,FREQ_P_V-3861,   4800,   1,0},//1889
        {FREQ_P_V,3854,FREQ_P_V-3854,   4420,   1,0},//1896
        {FREQ_P_V,3846,FREQ_P_V-3846,   5950,   1,0},//1904
        {FREQ_P_V,3834,FREQ_P_V-3834,   5400,   1,0},//1916
        {FREQ_P_V,3825,FREQ_P_V-3825,   6780,   1,0},//1925
        {FREQ_P_V,3807,FREQ_P_V-3807,   6000,   1,0},//1943
        {FREQ_P_V,3780,FREQ_P_V-3780,   27500,  1,0},//1970
        {FREQ_P_V,3740,FREQ_P_V-3740,   27500,  1,0},//2010
        {FREQ_P_V,3680,FREQ_P_V-3680,   27500,  1,0},//2070
        {FREQ_P_V,3640,FREQ_P_V-3640,   27500,  1,0},//2110
        {FREQ_P_V,3600,FREQ_P_V-3600,   27500,  1,0},//2150
                                        },
            //亚太V号
    {
       //H
      {10600,12294,12294-10600,3150, 0,0},//1694
        {10600,12325,12325-10600,4999,  0,0},//1725
        {10600,12338,12338-10600,7665,  0,0},//1738
        {10600,12383,12383-10600,2399,  0,0},//1783
        {10600,12433,12433-10600,3329,  0,0},//1833
        {10600,12456,12456-10600,4050,  0,0},//1856
        {10600,12633,12633-10600,43199, 0,0},//2033
        {10600,12445,12445-10600,1537,  0,0},//1845
        {10600,12693,12693-10600,43198, 0,0},//2093
        {10600,12321,12321-10600,1500,  0,0},//1721
        {10600,12281,12281-10600,3149, 0,0},//1681
            //V
        {10600,12278,12278-10600,4999,  1,0},//1678
        {10600,12307,12307-10600,30000,  1,0},//1707
        {10600,12358,12358-10600,43000,  1,0},//1758
        {10600,12404,12404-10600,22430, 1,0},//1804
        {10600,12433,12433-10600,22430, 1,0},//1833
        {10600,12476,12476-10600,33500, 1,0},//1876
        {10600,12541,12541-10600,41250, 1,0},//1941
        {10600,12603,12603-10600,42999, 1,0},//2003
        {10600,12649,12649-10600,22419, 1,0},//2049
        {10600,12672,12672-10600,4284,  1,0},//2072
        {10600,12688,12688-10600,3000,  1,0},//2088
        {10600,12724,12724-10600,42997, 1,0},//2124
                                        },
            //新天6号
    {
        {9750,11640,11640-9750,27500,  0,0},//1890
    {9750,11681,11681-9750,27499,  0,0},//1931
    {10750,12659,12659-10750,28000, 0,0},//1909
    {10750,12700,12700-10750,28000, 0,0},//1950
        {10750,12741,12741-10750,26403,  0,0},//1991
       //
    {9750,11135,11135-9750,30000,   1,0},//1385
    {9750,11177,11177-9750,30000,   1,0},//1427
    {9750,11480,11480-9750,3159,    1,0},//1730
    {9750,11546,11546-9750,43198,   1,0},//1796
    {9750,11681,11681-9750,28799,   1,0},//1931
    {10750,12659,12659-10750,28000, 1,0},//1909
    {10750,12699,12699-10750,28000, 1,0},//1949
    {10750,12741,12741-10750,26403, 1,0},//1991
                                                },

        //马星3号
    {
        {5150,4166,5150-4166,20640, 0,0},//984
        {5150,4122,5150-4122,30000, 0,0},//1028
        {5150,4042,5150-4042,28600, 0,0},//1108
        {5150,4002,5150-4002,30000, 0,0},//1148
        {5150,3736,5150-3736,3000, 0,0},//1414
        {5150,3732,5150-3732,3199, 0,0},//1418
      {5150,3712,5150-3712,2857, 0,0},//1438
      {5150,3707,5150-3707,4290, 0,0},//1443
//
        {5150,4122,5150-4122,29720, 1,0},//1028
        {5150,4085,5150-4085,10000, 1,0},//1065
        {5150,4057,5150-4057,2499, 1,0},//1093
        {5150,4044,5150-4044,2499, 1,0},//1106
        {5150,4004,5150-4004,5000, 1,0},//1146
        {5150,3922,5150-3922,29720, 1,0},//1228
        {5150,3892,5150-3892,2916, 1,0},//1258
        {5150,3879,5150-3879,18384, 1,0},//1271
        {5150,3866,5150-3866,2916, 1,0},//1284
        {5150,3842,5150-3842,29720, 1,0},//1308
        {5150,3816,5150-3816,6660, 1,0},//1334
        {5150,3807,5150-3807,3255, 1,0},//1343
        {5150,3797,5150-3797,5063, 1,0},//1353
        {5150,3789,5150-3789,7030, 1,0},//1361
        {5150,3762,5150-3762,29720, 1,0},//1388
        {5150,3723,5150-3723,2169, 1,0},//1427
        {5150,3642,5150-3642,13333, 1,0},//1508
    {5150,3632,5150-3632,2199, 1,0},//1518
    {5150,3618,5150-3618,2169, 1,0},//1532
    {5150,3614,5150-3614,3299, 1,0},//1536
                                     },
                  //越星1号
    {
        {9750,11421,11421-9750,18000, 0,0},//1671
        {9750,11441,11441-9750,4340, 0,0},//1691
        {9750,11471,11471-9750,2169, 0,0},//1721
        {9750,11482,11482-9750,3599, 0,0},//1732
        {9750,11546,11546-9750,27500, 0,0},//1796
        {9750,11586,11586-9750,27500, 0,0},//1836
        {9750,11622,11622-9750,20500, 0,0},//1872
        {9750,11666,11666-9750,27500, 0,0},//1916
        {9750,11895,11895-9750,2859, 0,0},//2145
        {10600,12271,12271-10600,18000, 0,0},//1671
        {10600,12291,12291-10600,4340, 0,0},//1691
        {10600,12321,12321-10600,2169, 0,0},//1721
        {10600,12332,12332-10600,3599, 0,0},//1732
        {10600,12396,12396-10600,27500, 0,0},//1796
        {10600,12436,12436-10600,27500, 0,0},//1836
        {10600,12473,12473-10600,20500, 0,0},//1873
        {10600,12516,12516-10600,27500, 0,0},//1916
        {10600,12551,12551-10600,19110, 0,0},//1951
        {10600,12596,12596-10600,27500, 0,0},//1996
        {10600,12676,12676-10600,27500, 0,0},//2076
        {10600,12745,12745-10600,2859, 0,0},//2145
//
    {9750,11421,11421-9750,18000, 1,0},//1671
        {9750,11441,11441-9750,4340, 1,0},//1691
        {9750,11471,11471-9750,2169, 1,0},//1721
        {9750,11482,11482-9750,3599, 1,0},//1732
        {9750,11546,11546-9750,27500, 1,0},//1796
        {9750,11586,11586-9750,27500, 1,0},//1836
        {9750,11622,11622-9750,20500, 1,0},//1872
        {9750,11666,11666-9750,27500, 1,0},//1916
        {9750,11895,11895-9750,2859, 1,0},//2145
        {10600,12271,12271-10600,18000, 1,0},//1671
        {10600,12291,12291-10600,4340, 1,0},//1691
        {10600,12321,12321-10600,2169, 1,0},//1721
        {10600,12332,12332-10600,3599, 1,0},//1732
        {10600,12396,12396-10600,27500, 1,0},//1796
        {10600,12436,12436-10600,27500, 1,0},//1836
        {10600,12473,12473-10600,20500, 1,0},//1873
        {10600,12516,12516-10600,27500, 1,0},//1916
        {10600,12551,12551-10600,19110, 1,0},//1951
        {10600,12596,12596-10600,27500, 1,0},//1996
        {10600,12676,12676-10600,27500, 1,0},//2076
        {10600,12745,12745-10600,2859, 1,0},//2145
                                        },
                //亚太2R
    {
            //H
        {5150,4175,5150-4175,4285, 0,0},//975
        {5150,4169,5150-4169,4285, 0,0},//981
        {5150,4163,5150-4163,4340, 0,0},//987
        {5150,4145,5150-4145,3600, 0,0},//1005
        {5150,4136,5150-4136,3333, 0,0},//1014
        {5150,4133,5150-4133,3750, 0,0},//1017
        {5150,4126,5150-4126,4441, 0,0},//1024
        {5150,4119,5150-4119,4444, 0,0},//1031
        {5150,4113,5150-4113,3695, 0,0},//1037
        {5150,4109,5150-4109,2000, 0,0},//1041
        {5150,4105,5150-4105,3125, 0,0},//1045
        {5150,4085,5150-4085,2530, 0,0},//1065
        {5150,4070,5150-4070,13020, 0,0},//1080
        {5150,4056,5150-4056,3212, 0,0},//1094
        {5150,4049,5150-4049,4290, 0,0},//1101
        {5150,4034,5150-4034,4300, 0,0},//1116
        {5150,4016,5150-4016,4340, 0,0},//1134
        {5150,4009,5150-4009,4290, 0,0},//1141
        {5150,4004,5150-4004,4324, 0,0},//1146
        {5150,3997,5150-3997,3190, 0,0},//1153
        {5150,3985,5150-3985,3700, 0,0},//1165
        {5150,3960,5150-3960,30000, 0,0},//1190
        {5150,3920,5150-3920,28340, 0,0},//1230
        {5150,3880,5150-3880,28125, 0,0},//1270
        {5150,3840,5150-3840,27500, 0,0},//1310
        {5150,3815,5150-3815,3333, 0,0},//1335
        {5150,3811,5150-3811,3980, 0,0},//1339
        {5150,3805,5150-3805,4340, 0,0},//1345
        {5150,3799,5150-3799,4330, 0,0},//1351
        {5150,3794,5150-3793,4444, 0,0},//1357
        {5150,3787,5150-3787,4600, 0,0},//1363
    {5150,3769,5150-3769,13333, 0,0},//1381
        {5150,3750,5150-3750,5000, 0,0},//1400
        {5150,3745,5150-3745,3125, 0,0},//1405
        {5150,3720,5150-3720,29620, 0,0},//1430
        {5150,3680,5150-3680,27500, 0,0},//1470
        {5150,3648,5150-3648,6500, 0,0},//1502
        {5150,3600,5150-3600,26999, 0,0},//1550
        {5150,3568,5150-3568,1852, 0,0},//1582
        {5150,3563,5150-3563,4443, 0,0},//1587
        {5150,3551,5150-3551,13334, 0,0},//1599
        {5150,3520,5150-3520,28130, 0,0},//1630
        {5150,3440,5150-3440,26669, 0,0},//1710
            //V
        {5150,4161,5150-4161,2000, 1,0},//989
        {5150,4109,5150-4109,2000, 1,0},//1041
        {5150,4031,5150-4031,6312, 1,0},//1119
        {5150,3993,5150-3993,1481, 1,0},//1157
        {5150,3958,5150-3958,1666, 1,0},//1192
        {5150,3765,5150-3765,5900, 1,0},//1385
        {5150,3755,5150-3755,4640, 1,0},//1395
        {5150,3747,5150-3747,8000, 1,0},//1403
        {5150,3705,5150-3705,8888, 1,0},//1445
        {5150,3463,5150-3463,2857, 1,0},//1687
        {5150,3458,5150-3458,2222, 1,0},//1692
        {5150,3426,5150-3426,2592, 1,0},//1724
        {5150,3419,5150-3419,3332, 1,0},//1731
        {5150,3415,5150-3415,2915, 1,0},//1735
        {5150,3409,5150-3409,4444, 1,0},//1741
                                     },
                //亚洲3S
    {
        {5150,4177,5150-4177,   2444,   0,0},//973
        {5150,4172,5150-4172,   2480,   0,0},//978
        {5150,4165,5150-4165,   5710,   0,0},//985
        {5150,4160,5150-4160,   3030,   0,0},//990
        {5150,4154,5150-4154,   5632,   0,0},//996
        {5150,4146,5150-4146,   4000,   0,0},//1004
        {5150,4132,5150-4132,   9200,   0,0},//1018
        {5150,4097,5150-4097,   2256,   0,0},//1053
        {5150,4071,5150-4071,   14240,  0,0},//1079
        {5150,4040,5150-4040,   26500,  0,0},//1110
        {5150,4000,5150-4000,   26850,  0,0},//1150
        {5150,3960,5150-3960,   27500,  0,0},//1190
        {5150,3880,5150-3880,   27500,  0,0},//1270
        {5150,3760,5150-3760,   26000,  0,0},//1390
        {5150,3706,5150-3706,   6000,   0,0},//1444
//
        {5150,4176,5150-4176,   2433,   1,0},//974
        {5150,4173,5150-4173,   2480,   1,0},//977
        {5150,4166,5150-4166,   5700,   1,0},//984
        {5150,4160,5150-4160,   5700,   1,0},//990
        {5150,4154,5150-4154,   5631,   1,0},//996
        {5150,4147,5150-4147,   4000,   1,0},//1003
        {5150,4132,5150-4132,   9200,   1,0},//1018
        {5150,4097,5150-4097,   2309,   1,0},//1053
        {5150,4072,5150-4072,   14240,  1,0},//1053
        {5150,4041,5150-4041,   26624,  1,0},//1109
        {5150,4001,5150-4001,   26850,  1,0},//1149
        {5150,3960,5150-3960,   27500,  1,0},//1190
        {5150,3881,5150-3881,   27500,  1,0},//1269
        {5150,3761,5150-3761,   25999,  1,0},//1389
        {5150,3707,5150-3707,   6000,   1,0},//1443
                                        },
#endif

};


struct nim_s3501_tp va_g3_ascan_real_tps[VD_TP_CNT];
UINT32 va_g3_ascan_real_tps_index;
struct nim_s3501_tp va_g3_ascan_lost_tps[VD_TP_CNT];
UINT32 va_g3_ascan_lost_tps_index;
struct nim_s3501_tp va_g3_ascan_war_tps[VD_TP_CNT];
UINT32 va_g3_ascan_war_tps_index;
struct nim_s3501_ascan vs_s3501_ascan=
{
    0,
    0,
    0,
    1,
    {0,0,0,0,0},  // equals VD_OSD_RPT_CNT ,5
    0,
    {0,0,0,0,0},// equals VD_OSD_RPT_CNT ,5
    1,
    {0,0,0,0,0},// equals VD_OSD_RPT_CNT ,5
    {0,0,0,0,0},// equals VD_OSD_RPT_CNT ,5
    900,
    2200,
    0,
    1,
    0,
};


static UINT32 tmp_lock=0x00;
static UINT32 ascan_stop_flag=0x00;
static UINT32 va_polar=0x00;
static UINT32 va_frq=0x00;
static UINT32 va_check_all=0x00;
static UINT32 lck_time_cal=0x00;
static struct as_module_info *ext_info=NULL;

static INT32 nim_s3501_ascan_init();
static INT32 nim_s3501_ascan_print();
INT32 nim_s3501_ascan_start_process();
INT32 nim_s3501_ascan_end_process();
static INT32 nim_s3501_ascan_add_tp(UINT32 type,UINT32 prt_freq,UINT32 freq, UINT32 sym, UINT32 polar);
static INT32 nim_s3501_ascan_tps_lock_check();
static UINT32 nim_s3501_ascan_convert_freq_proc(UINT32 freq,UINT32 get_check);

#define ASCAN_PRINTF  libc_printf
#endif

static UINT8   va_22k=0x00;

#define C3501C_NEW_TSO
//#define DISEQC_OUT_INVERT
#define C3501C_ERRJ_LOCK

static INT32 nim_s3501_WidebandScan(struct nim_device *dev,UINT32 start_freq, UINT32 end_freq);
static INT32 nim_s3501_WidebandScan_open(struct nim_device *dev,UINT32 start_freq, UINT32 end_freq,UINT32 step_freq);
static INT32 nim_s3501_WidebandScan_close();
extern INT32 	Frequency_Est[TP_MAXNUM];
extern INT32 	SymbolRate_Est[TP_MAXNUM];
extern INT32 	TP_number;
extern INT32     *channel_spectrum;
extern INT32     *channel_spectrum_tmp;
extern INT32     Last_Tuner_IF;
extern INT32     chlspec_Num;
extern INT32     called_num;

extern INT32     Final_Est_Freq;
extern INT32     Final_Est_SymRate;

extern INT32     Max_FFT_energy;

//#define SW_ADPT_CR
//#define SW_SNR_RPT_ONLY
#define HW_ADPT_CR
//#define HW_ADPT_CR_MONITOR

//#define DEBUG_SOFT_SEARCH      // Try someone TP for special case, for DEbug
//#define AUTOSCAN_DEBUG
#ifdef AUTOSCAN_DEBUG
    #define AUTOSCAN_PRINTF  libc_printf
    UINT16 config_data;
#else
    #define AUTOSCAN_PRINTF(...)
#endif

#define NIM_CAPTURE_SUPPORT

static UINT32 last_ch_fc=0,last_tp_freq=0;


extern INT32 FFT_energy_1024[1024];
extern INT32 FFT_energy_1024_tmp[1024];
static INT32 fft_energy_store[1024];

static INT32 snr_initial_en; 

/* Name for the tuner, the last character must be Number for index */
static char nim_s3501_name[3][HLD_MAX_NAME_SIZE] =
{
    "NIM_S3501_0", "NIM_S3501_1", "NIM_S3501_2"
};

static UINT8 S3501ID[10] =
{
    0x47, 0x55, 0x33, 0x35, 0x30, 0x31, 0X44, 0x41, 0x54, 0x41
};

static const UINT8 ssi_clock_tab[] =
{
    98, 90, 83, 77, 72, 67, 60, 54, 50
};

#ifdef NIM_CAPTURE_SUPPORT
static UINT32  capture_debug_en = 0;//use for capture
static UINT32  capture_startFreq = 950;
#endif

static const UINT8 MAP_BETA_ACTIVE_BUF[32] =
{
    0x00,   //                           //index 0, do not use
    0x01,   // 1/4 of QPSK        //1
    0x01,   // 1/3                      //2
    0x01,   // 2/5                      //3
    0x01,   // 1/2                      //4
    0x01,   // 3/5                      //5
    0x01,   // 2/3                      //6
    0x01,   // 3/4                      //7
    0x01,   // 4/5                      //8
    0x01,   // 5/6                      //9
    0x01,   // 8/9                      //a
    0x01,   // 9/10                     //b
    0x01,   // 3/5 of 8PSK           //c
    0x01,   // 2/3                      //d
    0x01,   // 3/4                      //e
    0x01,   // 5/6                      //f
    0x01,   // 8/9                      //10
    0x01,   // 9/10                     //11
    0x01,   // 2/3 of 16APSK        //12
    0x01,   // 3/4                      //13
    0x01,   // 4/5                      //14
    0x01,   // 5/6                      //15
    0x01,   // 8/9                      //16
    0x01,   // 9/10                     //17
    0x01,   // for 32 APSK, dont use
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
};

static const UINT8 MAP_BETA_BUF[32] =
{
    188,   //  205,     // index 0, do not use
    188,   //  205,     // 1/4 of QPSK    //1
    190,   //  230,     // 1/3                      //2
    205,   //  205,     // 2/5                      //3
    205,   //  205,     // 1/2                      //4
    180,   //  180,     // 3/5                      //5
    180,   //  180,     // 2/3                      //6
    180,   //  180,     // 3/4                      //7
    155,   //  180,     // 4/5                      //8
    168,   //  180,     // 5/6                      //9
    150,   //  155,     // 8/9                      //a
    150,   //  155,     // 9/10                     //b
    180,   //  180,     // 3/5 of 8PSK           //c
    180,   //  180,     // 2/3                      //d
    170,   //  180,     // 3/4                      //e
    180,   //  155,     // 5/6                      //f
    150,   //  155,     // 8/9                      //10
    150,   //  155,     // 9/10                     //11
    180,   //  205,     // 2/3 of 16APSK        //12
    180,   //  180,     // 3/4                      //13
    180,   //  180,     // 4/5                      //14
    170,   //  155,     // 5/6                      //15
    155,   //  155,     // 8/9                      //16
    155,   //  155,     // 9/10                     //17
    155,        //---------------------    for 32 APSK, dont use
    155, 155, 155, 155, 155, 155, 155
};

static const UINT16 DEMAP_NOISE[32] =
{
    0x00,       // index 0, do not use
    0x16b,      // 1/4 of QPSK          //1
    0x1d5,      // 1/3                      //2
    0x246,      // 2/5                      //3
    0x311,      // 1/2                      //4
    0x413,      // 3/5                      //5
    0x4fa,      // 2/3                      //6
    0x62b,      // 3/4                      //7
    0x729,      // 4/5                      //8
    0x80c,      // 5/6                      //9
    0xa2a,      // 8/9                      //a
    0xab2,      // 9/10                     //b
    0x8a9,      // 3/5 of 8PSK           //c
    0xb31,      // 2/3                      //d
    0xf1d,   // 3/4                      //e
    0x1501,     // 5/6                      //f
    0x1ca5,       // 8/9                      //10
    0x1e91,       // 9/10                     //11
    0x133b,       // 2/3 of 16APSK        //12
    0x199a,       // 3/4                      //13
    0x1f08,       // 4/5                      //14
    0x234f,       // 5/6                      //15
    0x2fa1,       // 8/9                      //16
    0x3291,        // 9/10                     //17
    0x00,        // for 32 APSK, dont use
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

static const UINT8 snr_tab[177] =
{
    0, 1, 3, 4, 5, 7, 8,\
    9, 10, 11, 13, 14, 15, 16, 17,\
    18, 19, 20, 21, 23, 24, 25, 26,\
    26, 27, 28, 29, 30, 31, 32, 33,\
    34, 35, 35, 36, 37, 38, 39, 39,\
    40, 41, 42, 42, 43, 44, 45, 45,\
    46, 47, 47, 48, 49, 49, 50, 51,\
    51, 52, 52, 53, 54, 54, 55, 55,\
    56, 57, 57, 58, 58, 59, 59, 60,\
    61, 61, 62, 62, 63, 63, 64, 64,\
    65, 66, 66, 67, 67, 68, 68, 69,\
    69, 70, 70, 71, 72, 72, 73, 73,\
    74, 74, 75, 76, 76, 77, 77, 78,\
    79, 79, 80, 80, 81, 82, 82, 83,\
    84, 84, 85, 86, 86, 87, 88, 89,\
    89, 90, 91, 92, 92, 93, 94, 95,\
    96, 96, 97, 98, 99, 100, 101, 102,\
    102, 103, 104, 105, 106, 107, 108, 109,\
    110, 111, 113, 114, 115, 116, 117, 118,\
    119, 120, 122, 123, 124, 125, 127, 128,\
    129, 131, 132, 133, 135, 136, 138, 139,\
    141, 142, 144, 145, 147, 148, 150, 152,\
    153, 155
};

#define TAB_SIZE 14 // for SI, use macro instead of enum

static const UINT16 SNR_THR[TAB_SIZE*2] = {
                    640, 670, // 6
                        576, 615, // 6.5
                        525, 563, // 7
                        479, 513, // 7.5
                        436, 468, // 8
                        397, 426, // 8.5
                        360, 388, // 9
                        328, 351, // 9.5
                        297, 319, // 10
                        267, 289, // 10.5
                        242, 261,  // 11
                        219, 234,  // 11.5
                        198, 213, // 12
                        120, 193 // 12.5
                    };
static const UINT16 CR_PARA_8PSK_3f5[TAB_SIZE] = {
            0x14b0,  //6
            0x14b0,  //6.5
            0x14b0,  //7
            0x13a0,  //7.5
            0x129a,  //8
            0x1295,  //8.5
            0x1295,  //9
            0x1295,  //9.5
            0x1295,  //10
            0x1295,  // 10.5
            0x1295,  // 11
            0x1295,  // 11.5
            0x1290,  // 12
            0x1290   // 12.5
        };
static const UINT16 CR_PARA_8PSK_others[TAB_SIZE] = {
            0x14b0, // 6
            0x14b0, // 6.5
            0x14b0, // 7
            0x13b0, // 7.5
            0x13a0, // 8
            0x13a0, // 8.5
            0x129a, // 9
            0x129a, // 9.5
            0x1295, // 10
            0x1295,  // 10.5
            0x1295, // 11
            0x1295, // 11.5
            0x1290, // 12
            0x1290  // 12.5
        };

//internal function
static DWORD nim_s3501_multu64div(UINT32 v1, UINT32 v2, UINT32 v3);
static INT32 nim_s3501_open(struct nim_device *dev);
static INT32 nim_s3501_close(struct nim_device *dev);
static INT32 nim_s3501_ioctl(struct nim_device *dev, INT32 cmd, UINT32 param);
static INT32 nim_s3501_set_polar(struct nim_device *dev, UINT8 polar);
static INT32 nim_s3501_set_12v(struct nim_device *dev, UINT8 flag);
static INT32 nim_s3501_channel_change(struct nim_device *dev, UINT32 freq, UINT32 sym, UINT8 fec);
static INT32 nim_s3501_channel_search(struct nim_device *dev, UINT32 CRNum);
static INT32 nim_s3501_DiSEqC_operate(struct nim_device *dev, UINT32 mode, UINT8 *cmd, UINT8 cnt);
static INT32 nim_s3501_DiSEqC2X_operate(struct nim_device *dev, UINT32 mode, UINT8 *cmd, UINT8 cnt, UINT8 *rt_value, UINT8 *rt_cnt);
static INT32 nim_s3501_get_lock(struct nim_device *dev, UINT8 *lock);
static INT32 nim_s3501_get_freq(struct nim_device *dev, UINT32 *freq);
static INT32 nim_s3501_get_freqoffset(struct nim_device *dev);
static INT32 nim_s3501_get_symbol_rate(struct nim_device *dev, UINT32 *sym_rate);
static INT32 nim_s3501_get_code_rate(struct nim_device *dev, UINT8 *code_rate);
static INT32 nim_s3501_get_AGC(struct nim_device *dev, UINT8 *agc);
static INT32 nim_s3501_get_SNR(struct nim_device *dev, UINT8 *snr);
static INT32 nim_s3501_get_BER(struct nim_device *dev, UINT32 *RsUbc);
static INT32 nim_s3501_get_PER(struct nim_device *dev, UINT32 *RsUbc);
static INT32 nim_s3501_get_LDPC(struct nim_device *dev, UINT32 *RsUbc);
static INT32 nim_s3501_get_fft_result(struct nim_device *dev, UINT32 freq, UINT32 *start_adr);
static INT32 nim_s3501_get_type(struct nim_device *dev);
static INT32 nim_s3501_reg_get_map_type(struct nim_device *dev, UINT8 *map_type);
static INT32 nim_s3501_reg_get_work_mode(struct nim_device *dev, UINT8 *work_mode);
static INT32 nim_s3501_reg_get_iqswap_flag(struct nim_device *dev, UINT8 *iqswap_flag);
static INT32 nim_s3501_reg_get_roll_off(struct nim_device *dev, UINT8 *roll_off);
static INT32 nim_s3501_reg_get_modcod(struct nim_device *dev, UINT8 *modcod);
static INT32 nim_s3501_get_dsp_clk(struct nim_device *dev, UINT32 *sample_rate);
static INT32 nim_s3501_set_dsp_clk(struct nim_device *dev, UINT8 clk_sel);
static  INT32 nim_s3501_get_tune_freq(struct nim_device *dev, INT32 *freq);
static  UINT8 nim_s3501_get_SNR_index(struct nim_device *dev);
static INT32 nim_s3501_set_ts_mode(struct nim_device *dev, UINT8 work_mode, UINT8 map_type, UINT8 code_rate, UINT32 Rs,
    UINT8 channel_change_flag);
static INT32 nim_s3501_get_bit_rate(struct nim_device *dev, UINT8 work_mode, UINT8 map_type, UINT8 code_rate, UINT32 Rs, UINT32 *bit_rate);
static INT32 nim_s3501_set_ssi_clk(struct nim_device *dev, UINT8 bit_rate);
static INT32 nim_s3501_set_dmy_format(struct nim_device *dev); 
static INT32 nim_s3501_ioctl_ext(struct nim_device *dev, INT32 cmd, void *param_list);
static INT32 nim_s3501_dynamic_power(struct nim_device *dev, UINT8 snr);
static INT32 nim_s3501_get_type(struct nim_device *dev);
static INT32 nim_s3501_set_err(struct nim_device *dev);
static INT32 nim_s3501_get_err(struct nim_device *dev);
static INT32 nim_s3501_clear_err(struct nim_device *dev);
static INT32 nim_s3501_i2c_open(struct nim_device *dev);
static INT32 nim_s3501_i2c_close(struct nim_device *dev);
static INT32 nim_s3501_ext_lnb_config(struct nim_device *dev, struct QPSK_TUNER_CONFIG_API *ptrQPSK_Tuner);
static INT32 nim_s3501_interrupt_mask_clean(struct nim_device *dev);
static INT32 nim_s3501_sym_config(struct nim_device *dev, UINT32 sym);
static INT32 nim_s3501_adc_setting(struct nim_device *dev);
static INT32 nim_s3501_set_hw_timeout(struct nim_device *dev, UINT8 time_thr);
static INT32 nim_s3501_agc1_ctrl(struct nim_device *dev, UINT8 low_sym, UINT8 s_Case);
static INT32 nim_s3501_freq_offset_set(struct nim_device *dev, UINT8 low_sym, UINT32 *s_Freq);
static INT32 nim_s3501_freq_offset_reset(struct nim_device *dev, UINT8 low_sym);
static INT32 nim_s3501_freq_offset_reset1(struct nim_device *dev, UINT8 low_sym, INT32 delfreq);
static INT32 nim_s3501_cr_setting(struct nim_device *dev, UINT8 s_Case);
static INT32 nim_s3501_ldpc_setting(struct nim_device *dev, UINT8 s_Case, UINT8 c_ldpc, UINT8 c_fec);
static INT32 nim_s3501_hw_init(struct nim_device *dev);
static INT32 nim_s3501_demod_ctrl(struct nim_device *dev, UINT8 c_Value);
static INT32 nim_s3501_hbcd_timeout(struct nim_device *dev, UINT8 s_Case);
static INT32 nim_s3501_set_acq_workmode(struct nim_device *dev, UINT8 s_Case);
static INT32 nim_s3501_set_FC_Search_Range(struct nim_device *dev, UINT8 s_Case, UINT32 rs);
static INT32 nim_s3501_RS_Search_Range(struct nim_device *dev, UINT8 s_Case, UINT32 rs);
static INT32 nim_s3501_cap_IQ_enerage(struct nim_device *dev);
static INT32 nim_s3501_TR_CR_Setting(struct nim_device *dev, UINT8 s_Case);
static INT32 nim_s3501_task_init(struct nim_device *dev);
static void nim_s3501_task(UINT32 param1, UINT32 param2);
static INT32 nim_s3501_set_phase_noise(struct nim_device *dev);
static INT32 nim_s3501_get_new_BER(struct nim_device *dev, UINT32 *ber);
static INT32 nim_s3501_get_new_PER(struct nim_device *dev, UINT32 *per);
static INT32 nim_s3501_get_phase_error(struct nim_device *dev, INT32 *phase_error);
static void nim_s3501_set_demap_noise(struct nim_device *dev);
static INT32 nim_s3501_s2_get_BER(struct nim_device *dev, UINT32 *RsUbc);
static INT32 nim_change_ts_gap(struct nim_device *dev, UINT8 gap);
static INT32 nim_s3501_get_bypass_buffer(struct nim_device *dev);
static INT32 nim_S3501_cap_start(struct nim_device *dev, UINT32 startFreq, UINT32 sym, UINT32 *cap_buffer);
static INT32 nim_sw_adaptive_cr(struct nim_device *dev);
static INT32 nim_sw_snr_rpt(struct nim_device *dev);
static INT32 nim_cr_tab_init(struct nim_device *dev);
static void nim_hw_adaptive_cr_monitor(struct nim_device *dev);
static INT32 nim_m3501c_invert_moerrj (struct nim_device *dev);
static INT32 nim_m3501c_recover_moerrj (struct nim_device *dev);
static INT32 nim_m3501c_close_dummy (struct nim_device *dev);
static INT32 nim_m3501c_open_dummy (struct nim_device *dev);
static INT32 nim_m3501c_dvbs_ts_bps (struct nim_device *dev);
static INT32 nim_m3501c_enable_soft_cbr (struct nim_device *dev);
static INT32 nim_m3501c_disable_soft_cbr (struct nim_device *dev);
static INT32 nim_m3501c_fec_ts_off (struct nim_device *dev);
static INT32 nim_m3501c_fec_ts_on (struct nim_device *dev);
static INT32 nim_m3501_ts_off (struct nim_device *dev);
static INT32 nim_m3501_ts_on (struct nim_device *dev);
static INT32 nim_m3501c_reset_tso (struct nim_device *dev);
static INT32 nim_m3501c_get_int(struct nim_device *dev);


/*---------------------------------------------------------
Name:
    multu64div
Description:
    This function implment v1*v2/v3. And v1*v2 is 64 bit
Parameters:
    [IN]
    [OUT]
Return:
---------------------------------------------------------*/
//Get HI value from the multple result
#ifdef WIN32
DWORD nim_s3501_multu64div(UINT32 v1, UINT32 v2, UINT32 v3)
{
    DWORD v;

    if (v3 == 0)
        return 0;

	v = ((UINT64)(v1)*(UINT64)(v2)+v3/2)/v3;

	return v;
}
#else
# define mult64hi(v1, v2)           \
({  DWORD __ret;            \
    __asm__ volatile(                   \
        "multu  %1, %2\n    "               \
        "mfhi %0\n" \
         : "=r" (__ret)         \
         : "r" (v1), "r"(v2));                  \
    __ret;                      \
})

//Get LO value from the multple result
# define mult64lo(v1, v2)           \
({  DWORD __ret;            \
    __asm__ volatile(                   \
        "multu  %1, %2\n    "               \
        "mflo %0\n" \
         : "=r" (__ret)         \
         : "r" (v1), "r"(v2));                  \
    __ret;                      \
})
DWORD nim_s3501_multu64div(UINT32 v1, UINT32 v2, UINT32 v3)
{
    DWORD hi, lo;
    UINT64 tmp;
    DWORD *tmp64;
    DWORD ret;

    if (v3 == 0)
        return 0;

    hi = mult64hi(v1, v2);
    lo = mult64lo(v1, v2);
    tmp64 = ((DWORD *)(&tmp))+1;
    *tmp64-- = hi;
    *tmp64 = lo;

    //Few nop here seems required, if no nop is here,
    //then the result wont be correct.
    //I guess maybe this is caused by some GCC bug.
    //Because I had checked the compiled result between with nop and without nop,
    //they are quite different!! I dont have time to search more GCC problem,
    //Therefore, I can only leave the nop here quietly. :(
    //--Michael 2003/10/10
    __asm__("nop; nop; nop; nop");
    ret = (tmp+v3/2)/v3;
    return ret;
}
#endif

//-------------------------------------

INT32 nim_reg_read(struct nim_device *dev, UINT8 bMemAdr, UINT8 *pData, UINT8 bLen)
{
    INT32 err;
    UINT8 chip_adr ;
    UINT32 i2c_type_id;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    chip_adr = priv->ext_dm_config.i2c_base_addr;
    i2c_type_id = priv->ext_dm_config.i2c_type_id;

    pData[0] = bMemAdr;
    NIM_MUTEX_ENTER(priv);
    err = i2c_write_read(i2c_type_id, chip_adr, pData, 1, bLen);
    NIM_MUTEX_LEAVE(priv);
    if (err)
    {
        if (priv->ul_status.m_pfn_reset_s3501)
        {
            priv->ul_status.m_pfn_reset_s3501((priv->tuner_id + 1) << 16);
            osal_task_sleep(100);
            priv->t_Param.t_i2c_err_flag = 0x01;
        }
        nim_s3501_set_err(dev);
        NIM_PRINTF("s3501 i2c read error = %d,chip_adr=0x%x,bMemAdr=0x%x,I2C_FOR_S3501 = %d,TaskID=%d\n", -err,chip_adr,pData[0],i2c_type_id,osal_task_get_current_id());
    }
    else
    {
        if (priv->t_Param.t_i2c_err_flag)
        {
            NIM_PRINTF("t_i2c_err_flag==1\n");
            priv->t_Param.t_i2c_err_flag = 0x00;
            nim_s3501_open(dev);
        }
    }
    return err;
}


INT32 nim_reg_write(struct nim_device *dev, UINT8 bMemAdr, UINT8 *pData, UINT8 bLen)
{
    int err;
    UINT8 i, buffer[8];
    UINT8 chip_adr ;
    UINT32 i2c_type_id;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    chip_adr = priv->ext_dm_config.i2c_base_addr;
    i2c_type_id = priv->ext_dm_config.i2c_type_id;

    if (bLen > 7)
    {
        nim_s3501_set_err(dev);
        return ERR_FAILUE;
    }
    buffer[0] = bMemAdr;
    for (i = 0; i < bLen; i++)
    {
        buffer[i + 1] = pData[i];
    }
    NIM_MUTEX_ENTER(priv);
    err = i2c_write(i2c_type_id, chip_adr, buffer, bLen + 1);
    NIM_MUTEX_LEAVE(priv);
    if (err != 0)
    {
        if (priv->ul_status.m_pfn_reset_s3501)
        {
            priv->ul_status.m_pfn_reset_s3501((priv->tuner_id + 1) << 16);
            osal_task_sleep(100);
            priv->t_Param.t_i2c_err_flag = 0x01;
        }
        nim_s3501_set_err(dev);
        NIM_PRINTF("s3501 i2c write error = %d,chip_adr=0x%x,bMemAdr=0x%x,I2C_FOR_S3501 = %d,TaskID=%d\n", -err,chip_adr,pData[0],i2c_type_id,osal_task_get_current_id());
    }
    else
    {
        if (priv->t_Param.t_i2c_err_flag)
        {
            NIM_PRINTF("t_i2c_err_flag==1\n");
            priv->t_Param.t_i2c_err_flag = 0x00;
            nim_s3501_open(dev);
        }
    }
    return err;
}

#ifdef NIM_3501_FUNC_EXT
void reg_read_verification(struct nim_device *dev)
{
    UINT8 ver_data;
    INT32 i, j, k;

    static UINT8 m_reg_list[192] =
    {
        0x11, 0x00, 0x00, 0x0f, 0x00, 0x5a, 0x50, 0x2f, 0x48, 0x3f, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0xec, 0x17, 0x1e,
        0x17, 0xec, 0x6b, 0x8e, 0xb1, 0x28, 0xba, 0x0c, 0x60, 0x00, 0x03, 0x05, 0x00, 0x20, 0x10, 0x04, 0x00, 0x00, 0xe3, 0x88, 0x4f, 0x2d,
        0x68, 0x06, 0x5a, 0x10, 0x77, 0xe5, 0x24, 0xaa, 0x45, 0x87, 0x51, 0x52, 0xba, 0x46, 0x80, 0x3e, 0x25, 0x14, 0x28, 0x3f, 0xc0, 0x00,
        0x02, 0x83, 0x00, 0x00, 0x00, 0x7f, 0x98, 0x48, 0x38, 0x58, 0xc2, 0x12, 0xd2, 0x00, 0x10, 0x27, 0x60, 0xea, 0x71, 0x00, 0x00, 0x32,
        0x10, 0x08, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0xb0, 0x30, 0x30, 0x59, 0x33, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x50, 0x5a, 0x57, 0x0b, 0x45, 0xc6, 0x41, 0x3c, 0x04, 0x20, 0x70,
        0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x35, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0xca, 0x7c, 0x21, 0x04, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00
    };

    for (i = 0; i <= 0xc7; i++)
    {
        if ((0x03 == i) ||
            (0x04 == i) ||
            (0x0b == i) ||
            (0x0c == i) ||
            (0x11 == i) ||
            (0x12 == i) ||
            (0x26 == i) ||
            (0x27 == i) ||
            (0x45 == i) ||
            (0x46 == i) ||
            (0x55 == i) ||
            (0x56 == i) ||
            (0x5a == i) ||
            (0x68 == i) ||
            (0x69 == i) ||
            (0x6a == i) ||
            (0x6b == i) ||
            (0x6c == i) ||
            (0x6d == i) ||
            (0x6e == i) ||
            (0x6f == i) ||
            (0x72 == i) ||
            (0x73 == i) ||
            (0x7d == i) ||
            (0x86 == i) ||
            (0x87 == i) ||
            (0x88 == i) ||
            (0x89 == i) ||
            (0x8a == i) ||
            (0x8b == i) ||
            (0x8c == i) ||
            (0x8d == i) ||
            (0x9d == i) ||
            (0xa3 == i) ||
            (0xa4 == i) ||
            (0xa5 == i) ||
            (0xa6 == i) ||
            (0xaa == i) ||
            (0xab == i) ||
            (0xac == i) ||
            (0xb2 == i) ||
            (0xb1 == i) ||
            (0xb4 == i) ||
            (0xba == i) ||
            (0xbb == i) ||
            (0xbc == i) ||
            (0xc0 == i) ||
            (0xc1 == i) ||
            (0xc4 == i) ||
            (0xc2 == i) ||
            (0xc3 == i))
        {
        }
        else
        {
            for (j = 0; j < 1000; j++)
            {
                nim_reg_read(dev, i, &ver_data, 1);
                for (k = 0; k < 1000; k++)
                {
                    j = j;
                }
                if (ver_data != m_reg_list[i])
                {
                    NIM_PRINTF(" read reg number :0x%x ; ver_data :0x%x ,reg_list_data : 0x%x\n", i, ver_data, m_reg_list[i]);
                }
            }
        }
    }
    NIM_PRINTF("finish register read test\n");

    return;
}

void reg_write_verification(struct nim_device *dev)
{
    UINT8 ver_data;
    UINT8 write_data;
    INT32 i;

    for (i = 0x0; i <= 0xc7; i++)
    {
        if ((0x00 == i) ||
            (0xb1 == i) ||
            (0x04 == i) ||
            (0x0b == i) ||
            (0x0c == i) ||
            (0x11 == i) ||
            (0x12 == i) ||
            (0x26 == i) ||
            (0x27 == i) ||
            (0x45 == i) ||
            (0x46 == i) ||
            (0x55 == i) ||
            (0x56 == i) ||
            (0x5a == i) ||
            (0x68 == i) ||
            (0x69 == i) ||
            (0x6a == i) ||
            (0x6b == i) ||
            (0x6c == i) ||
            (0x6d == i) ||
            (0x6e == i) ||
            (0x6f == i) ||
            (0x72 == i) ||
            (0x73 == i) ||
            (0x7d == i) ||
            (0x86 == i) ||
            (0x87 == i) ||
            (0x88 == i) ||
            (0x89 == i) ||
            (0x8a == i) ||
            (0x8b == i) ||
            (0x8c == i) ||
            (0x8d == i) ||
            (0x9d == i) ||
            (0xa3 == i) ||
            (0xa4 == i) ||
            (0xa5 == i) ||
            (0xa6 == i) ||
            (0xaa == i) ||
            (0xab == i) ||
            (0xac == i) ||
            (0xb2 == i) ||
            (0xb3 == i) ||
            (0xb4 == i) ||
            (0xba == i) ||
            (0xbb == i) ||
            (0xbc == i) ||
            (0xc2 == i) ||
            (0xc3 == i) ||
            (0xc4 == i))
        {
        }
        else
        {
            NIM_PRINTF(" i=%d\n", i);
            write_data = 0x5a;
            nim_reg_write(dev, i, &write_data, 1);
            nim_reg_read(dev, i, &ver_data, 1);

            if (ver_data != write_data)
            {
                NIM_PRINTF(" write reg number :0x%x ; write data : %x; ver_data :0x%x\n", i, write_data, ver_data);
            }
            write_data = 0xa6;
            nim_reg_write(dev, i, &write_data, 1);

            nim_reg_read(dev, i, &ver_data, 1);
            if (ver_data != write_data)
            {
                NIM_PRINTF(" write reg number :0x%x  ; write data : %x; ver_data :0x%x\n", i, write_data, ver_data);
            }
        }
    }
    NIM_PRINTF("reg_write_verfication all finish\n");
    return;
}
#endif

/*****************************************************************************
* INT32 nim_s3501_attach (struct QPSK_TUNER_CONFIG_API * ptrQPSK_Tuner)
* Description: S3501 initialization
*
* Arguments:
*  none
*
* Return Value: INT32
*****************************************************************************/
INT32 nim_s3501_attach(struct QPSK_TUNER_CONFIG_API *ptrQPSK_Tuner)
{
    struct nim_device *dev;
    struct nim_s3501_private *priv_mem;
    static unsigned char nim_dev_num = 0;

    NIM_PRINTF("Enter nim_s3501_attach:\n");

    if (ptrQPSK_Tuner == NULL)
    {
        NIM_PRINTF("Tuner Configuration API structure is NULL!/n");
        return ERR_NO_DEV;
    }
    if (nim_dev_num > 2)
    {
        NIM_PRINTF("Can not support three or more S3501 !/n");
        return ERR_NO_DEV;
    }

    dev = (struct nim_device *) dev_alloc(nim_s3501_name[nim_dev_num], HLD_DEV_TYPE_NIM, sizeof(struct nim_device));
    if (dev == NULL)
    {
        NIM_PRINTF("Error: Alloc nim device error!\n");
        return ERR_NO_MEM;
    }

    priv_mem = (struct nim_s3501_private *) comm_malloc(sizeof(struct nim_s3501_private));
    if (priv_mem == NULL)
    {
        dev_free(dev);
        NIM_PRINTF("Alloc nim device prive memory error!/n");
        return ERR_NO_MEM;
    }
    comm_memset(priv_mem, 0, sizeof(struct nim_s3501_private));
    dev->priv = (void *) priv_mem;
    //diseqc state init
    dev->diseqc_info.diseqc_type = 0;
    dev->diseqc_info.diseqc_port = 0;
    dev->diseqc_info.diseqc_k22 = 0;

    if ((ptrQPSK_Tuner->config_data.QPSK_Config & M3501_POLAR_REVERT) == M3501_POLAR_REVERT) //bit4: polarity revert.
        dev->diseqc_info.diseqc_polar = LNB_POL_V;
    else //default usage, not revert.
        dev->diseqc_info.diseqc_polar = LNB_POL_H;

    dev->diseqc_typex = 0;
    dev->diseqc_portx = 0;

    /* Function point init */
    dev->base_addr = ptrQPSK_Tuner->ext_dm_config.i2c_base_addr;
    dev->init = nim_s3501_attach;
    dev->open = nim_s3501_open;
    dev->stop = nim_s3501_close;
    dev->do_ioctl = nim_s3501_ioctl;
    dev->do_ioctl_ext = nim_s3501_ioctl_ext;
    dev->get_lock = nim_s3501_get_lock;
    dev->get_freq = nim_s3501_get_freq;
    dev->get_FEC = nim_s3501_get_code_rate;
    dev->get_SNR = nim_s3501_get_SNR;

#if ( SYS_PROJECT_FE == PROJECT_FE_DVBS||SYS_PROJECT_FE == PROJECT_FE_DVBS2)
    dev->set_polar = nim_s3501_set_polar;
    dev->set_12v = nim_s3501_set_12v;
    dev->channel_search = nim_s3501_channel_search;
    dev->DiSEqC_operate = nim_s3501_DiSEqC_operate;
    dev->DiSEqC2X_operate = nim_s3501_DiSEqC2X_operate;
    dev->get_sym = nim_s3501_get_symbol_rate;
    dev->get_AGC = nim_s3501_get_AGC;
    dev->get_BER = nim_s3501_get_BER;
    dev->get_fft_result = nim_s3501_get_fft_result;
    dev->get_ver_infor = NULL;
#endif
    /* tuner configuration function */
    priv_mem->nim_Tuner_Init = ptrQPSK_Tuner->nim_Tuner_Init;
    priv_mem->nim_Tuner_Control = ptrQPSK_Tuner->nim_Tuner_Control;
    priv_mem->nim_Tuner_Status = ptrQPSK_Tuner->nim_Tuner_Status;
    priv_mem->i2c_type_id = ptrQPSK_Tuner->tuner_config.i2c_type_id;

    priv_mem->Tuner_Config_Data.QPSK_Config = ptrQPSK_Tuner->config_data.QPSK_Config;
    priv_mem->ext_dm_config.i2c_type_id = ptrQPSK_Tuner->ext_dm_config.i2c_type_id;
    priv_mem->ext_dm_config.i2c_base_addr = ptrQPSK_Tuner->ext_dm_config.i2c_base_addr;

    priv_mem->ul_status.m_enable_dvbs2_hbcd_mode = 0;
    priv_mem->ul_status.m_dvbs2_hbcd_enable_value = 0x7f;
    priv_mem->ul_status.nim_s3501_sema = OSAL_INVALID_ID;
    priv_mem->ul_status.s3501_autoscan_stop_flag = 0;
    priv_mem->ul_status.s3501_chanscan_stop_flag = 0;
    priv_mem->ul_status.old_ber = 0;
    priv_mem->ul_status.old_per = 0;
    priv_mem->ul_status.m_hw_timeout_thr = 0;
    priv_mem->ul_status.old_ldpc_ite_num = 0;
    priv_mem->ul_status.c_RS = 0;
    priv_mem->ul_status.phase_err_check_status = 0;
    priv_mem->ul_status.s3501d_lock_status = NIM_LOCK_STUS_NORMAL;
    priv_mem->ul_status.m_tso_status = NIM_TSO_STUS_UNLOCK;
    priv_mem->ul_status.m_s3501_type = 0x00;
    priv_mem->ul_status.m_setting_freq = 123;
    priv_mem->ul_status.m_Err_Cnts = 0x00;
    priv_mem->tsk_status.m_lock_flag = NIM_LOCK_STUS_NORMAL;
    priv_mem->tsk_status.m_task_id = 0x00;
    priv_mem->t_Param.t_aver_snr = -1;
    priv_mem->t_Param.t_last_iter = -1;
    priv_mem->t_Param.t_last_snr = -1;
    priv_mem->t_Param.t_snr_state = 0;
    priv_mem->t_Param.t_snr_thre1 = 256;
    priv_mem->t_Param.t_snr_thre2 = 256;
    priv_mem->t_Param.t_snr_thre3 = 256;
    priv_mem->t_Param.t_dynamic_power_en = 0;
    priv_mem->t_Param.t_phase_noise_detected = 0;
    priv_mem->t_Param.t_reg_setting_switch = 0x0f;
    priv_mem->t_Param.t_i2c_err_flag = 0x00;
    priv_mem->flag_id = OSAL_INVALID_ID;

    priv_mem->blscan_mode = NIM_SCAN_SLOW;

    /* Add this device to queue */
    if (dev_register(dev) != SUCCESS)
    {
        NIM_PRINTF("Error: Register nim device error!\n");
        comm_free(priv_mem);
        dev_free(dev);
        return ERR_NO_DEV;
    }
	
    nim_dev_num++;
    priv_mem->ul_status.nim_s3501_sema = NIM_MUTEX_CREATE(1);

	priv_mem->m3501_mutex= osal_mutex_create();
	if(priv_mem->m3501_mutex == OSAL_INVALID_ID)
	{
		NIM_PRINTF("Error: Register nim device error!\n");
		return ERR_FAILUE;
	}


    if (nim_s3501_i2c_open(dev))
    {
		return S3501_ERR_I2C_NO_ACK;
    }	

    // Initial the QPSK Tuner
    if (priv_mem->nim_Tuner_Init != NULL)
    {
        NIM_PRINTF(" Initial the Tuner \n");
        if (((struct nim_s3501_private *) dev->priv)->nim_Tuner_Init(&priv_mem->tuner_id, &(ptrQPSK_Tuner->tuner_config)) != SUCCESS)
        {
            NIM_PRINTF("Error: Init Tuner Failure!\n");

            if (nim_s3501_i2c_close(dev))
                return S3501_ERR_I2C_NO_ACK;

            return ERR_NO_DEV;
        }
    }

    if (nim_s3501_i2c_close(dev))
        return S3501_ERR_I2C_NO_ACK;

    nim_s3501_ext_lnb_config(dev, ptrQPSK_Tuner);

    nim_s3501_get_type(dev);

    if (priv_mem->ul_status.m_s3501_type == NIM_CHIP_ID_M3501A &&    
        (priv_mem->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_2BIT_MODE)
    {
        if(SYS_MAIN_BOARD == BOARD_DB_M3606_01V01)
        {
            priv_mem->Tuner_Config_Data.QPSK_Config &= 0x3f; 
            NIM_PRINTF("M3501A SSI 2bit mode, auto change to 1bit mode\n");
        }
    }

    ptrQPSK_Tuner->device_type = priv_mem->ul_status.m_s3501_type;

    NIM_PRINTF("Leave nim_s3501_attach\n");

    return SUCCESS;
}

INT32 nim_s3501_hw_check(struct nim_device *dev)
{
    UINT8 data;
    NIM_PRINTF("Enter nim_s3501_hw_check:\n");

    nim_reg_read(dev, RA3_CHIP_ID + 0x01, &data, 1);
    if (data != 0x35)
        return ERR_FAILED;
    else
        return SUCCESS;
}

static void nim_s3501_set_demap_noise(struct nim_device *dev)
{
    UINT8 data, noise_index;
    UINT16 est_noise;

    // activate noise
    nim_reg_read(dev, RD0_DEMAP_NOISE_RPT + 2, &data, 1);
    data &= 0xfc;
    nim_reg_write(dev, RD0_DEMAP_NOISE_RPT + 2, &data, 1);

    // set noise_index
    noise_index = 0x0c;
    nim_reg_write(dev, RD0_DEMAP_NOISE_RPT + 1, &noise_index, 1);

    // set noise
    est_noise = DEMAP_NOISE[noise_index];
    data = est_noise & 0xff;
    nim_reg_write(dev, RD0_DEMAP_NOISE_RPT, &data, 1);
    data = (est_noise >> 8) & 0x3f;
    data |= 0xc0;
    nim_reg_write(dev, RD0_DEMAP_NOISE_RPT + 1, &data, 1);
}

void nim_s3501_after_reset_set_param(struct nim_device *dev)
{
    UINT8 data;
    UINT32 data_tmp;
    int i;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;

    NIM_PRINTF("Enter nim_s3501_after_reset_set_param:\n");
    nim_s3501_demod_ctrl(dev, NIM_DEMOD_CTRL_0X91);

    nim_s3501_interrupt_mask_clean(dev);

    data = 0x50; 
    nim_reg_write(dev, R0A_AGC1_LCK_CMD, &data, 1);

    // set diseqc clock.
    nim_s3501_get_dsp_clk(dev, &data_tmp);
    // Set diseqc parameter
    data=((data_tmp+500)/1000);
    nim_reg_write(dev, R90_DISEQC_CLK_RATIO, &data, 1);

    // set unconti timer
    data = 0x40;
    nim_reg_write(dev, RBA_AGC1_REPORT, &data, 1);

    //set receive timer: 0x88 for default;
    data = 0x88;
    nim_reg_write(dev, R8E_DISEQC_TIME, &data, 1);
    data = 0xff;
    nim_reg_write(dev, R8E_DISEQC_TIME + 0x01, &data, 1);

    if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
    {

        if((priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C))
        {
#ifdef DISEQC_OUT_INVERT
            data = 0xbc;
            nim_reg_write(dev, R90_DISEQC_CLK_RATIO+6, &data, 1);
#endif

            // TSO setting
            // Disable TSO OEJ
            data =0x6f;
            nim_reg_write(dev, RF0_HW_TSO_CTRL+5, &data, 1);

            if((priv->ul_status.m_tso_mode == 1))
            {
                data = 0x13;
                nim_reg_write(dev, RF0_HW_TSO_CTRL, &data, 1); 

                data = 0x08;
                nim_reg_write(dev, RF1_DSP_CLK_CTRL, &data, 1);

                if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_2BIT_MODE)
                {
                    data = 0x00;
                    nim_reg_write(dev, RF0_HW_TSO_CTRL+6, &data, 1);
                }
                else
                {
                    // Invert MOCLK
                    data = 0x01;
                    nim_reg_write(dev, RF0_HW_TSO_CTRL+6, &data, 1);
                }

#ifdef C3501C_ERRJ_LOCK
                nim_m3501c_invert_moerrj(dev);
#endif
                // Enable TSO_RESET_LEVEL
                data = 0xef;
                nim_reg_write(dev, RFF_TSO_CLS, &data, 1);

                nim_m3501c_fec_ts_on(dev);

                if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_1BIT_MODE)
                    data = 0xc0;
                else if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_2BIT_MODE)
                    data = 0x00;
                else if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_4BIT_MODE)
                    data = 0x40;
                else 
                    data = 0x80;
                nim_reg_write(dev, RC0_BIST_LDPC_REG, &data, 1);

                if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_1BIT_MODE)
                    data = 0x70;
                else if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_2BIT_MODE)
                    data = 0x11;
                else if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_4BIT_MODE)
                    data = 0x32;
                else 
                    data = 0x50;
                data = data | 0x80;
                nim_reg_write(dev, RBD_CAP_PRM, &data, 1); //set ts_port enable

                // FEC use SPI output to TSO, use fast speed
                data = 0xc0;
                nim_reg_write(dev, RAD_TSOUT_SYMB+1, &data, 1);
                data = 0x02;
                nim_reg_write(dev, RAD_TSOUT_SYMB, &data, 1);
            }
            else
            {
                data = 0x10;
                nim_reg_write(dev, RF0_HW_TSO_CTRL, &data, 1);
                if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_1BIT_MODE)
                    data = 0x70;
                else if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_2BIT_MODE)
                    data = 0x11;
                else if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_4BIT_MODE)
                    data = 0x32;
                else 
                    data = 0x50;
                nim_reg_write(dev, RBD_CAP_PRM, &data, 1);

                if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_2BIT_MODE)
                {
                    data = 0xf1;
                    nim_reg_write(dev, R54_VITERBI_FRAME_SYNC, &data, 1);
                    data = 0x0e;
                    nim_reg_write(dev, RD8_TS_OUT_SETTING, &data, 1);
                    data = 0x00;
                    nim_reg_write(dev, RF0_HW_TSO_CTRL+6, &data, 1);
                }
                else
                {
                    data = 0x71;
                    nim_reg_write(dev, R54_VITERBI_FRAME_SYNC, &data, 1);
                    data = 0x06;
                    nim_reg_write(dev, RD8_TS_OUT_SETTING, &data, 1);
                    data = 0x00;
                    nim_reg_write(dev, RF0_HW_TSO_CTRL+6, &data, 1);
                }
            }
        }
        else
        {
            if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_1BIT_MODE)
                data = 0x70;
            else if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_2BIT_MODE)
                data = 0x11;
            else if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_4BIT_MODE)
                data = 0x32;
            else 
                data = 0x50;
            nim_reg_write(dev, RBD_CAP_PRM, &data, 1);

            if((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_2BIT_MODE)
            {
                data = 0xf1;
                nim_reg_write(dev, R54_VITERBI_FRAME_SYNC, &data, 1);
                data = 0x0e;
                nim_reg_write(dev, RD8_TS_OUT_SETTING, &data, 1);
            }
            else
            {
                data = 0x71;
                nim_reg_write(dev, R54_VITERBI_FRAME_SYNC, &data, 1);
                data = 0x06;
                nim_reg_write(dev, RD8_TS_OUT_SETTING, &data, 1);
            }
        }

        for (i = 0; i < 32; i++)
        {
            data = i;
            nim_reg_write(dev, R9C_DEMAP_BETA + 0x02, &data, 1);
            data = MAP_BETA_ACTIVE_BUF[i];
            nim_reg_write(dev, R9C_DEMAP_BETA, &data, 1);
            data = 0x04;
            nim_reg_write(dev, R9C_DEMAP_BETA + 0x03, &data, 1);
            data = MAP_BETA_BUF[i];
            nim_reg_write(dev, R9C_DEMAP_BETA, &data, 1);
            data = 0x03;
            nim_reg_write(dev, R9C_DEMAP_BETA + 0x03, &data, 1);
        }

    }
    return ;
}

/*****************************************************************************
* INT32 nim_s3501_open(struct nim_device *dev)
* Description: S3501 open
*
* Arguments:
*  Parameter1: struct nim_device *dev
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_s3501_open(struct nim_device *dev)
{
    INT32 ret;
    UINT8 data;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;

    NIM_PRINTF("Enter nim_s3501_open:\n");
    nim_s3501_set_acq_workmode(dev, NIM_OPTR_HW_OPEN);

    if(priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B && priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C)
    {
        // open clock
        nim_reg_read(dev, RCE_TS_FMT_CLK+1, &data, 1);
        data = data | 0x01;
        nim_reg_write(dev, RCE_TS_FMT_CLK+1, &data, 1);
        nim_reg_read(dev, R5B_ACQ_WORK_MODE, &data, 1);
        data = data | 0x40;
        nim_reg_write(dev, R5B_ACQ_WORK_MODE, &data, 1);
    
        // open rxadc
        nim_reg_read(dev, RA0_RXADC_REG+2, &data, 1);
        data = data & 0xf8;
        nim_reg_write(dev, RA0_RXADC_REG+2, &data, 1);
    }  

    ret = nim_s3501_hw_check(dev);
    if (ret != SUCCESS)
        return ret;
    ret = nim_s3501_hw_init(dev);

    nim_s3501_after_reset_set_param(dev);

    nim_s3501_hbcd_timeout(dev, NIM_OPTR_HW_OPEN);

    nim_s3501_task_init(dev);

#ifdef CHANNEL_CHANGE_ASYNC
    if (priv->flag_id == OSAL_INVALID_ID)
        priv->flag_id = NIM_FLAG_CREATE(0);
#endif
    NIM_PRINTF("Leave nim_s3501_open\n");
    return SUCCESS;
}

/*****************************************************************************
* INT32 nim_s3501_close(struct nim_device *dev)
* Description: S3501 close
*
* Arguments:
*  Parameter1: struct nim_device *dev
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_s3501_close(struct nim_device *dev)
{
    UINT8 data;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;

    nim_s3501_demod_ctrl(dev, NIM_DEMOD_CTRL_0X90);

    nim_s3501_set_acq_workmode(dev, NIM_OPTR_HW_CLOSE);

    if(priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B && priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C)
    {
        // close clock
        nim_reg_read(dev, RCE_TS_FMT_CLK+1, &data, 1);
        data = data & 0xfe;
        nim_reg_write(dev, RCE_TS_FMT_CLK+1, &data, 1);
        nim_reg_read(dev, R5B_ACQ_WORK_MODE, &data, 1);
        data = data & 0xbf;
        nim_reg_write(dev, R5B_ACQ_WORK_MODE, &data, 1);
    
        // close rxadc
        nim_reg_read(dev, RA0_RXADC_REG+2, &data, 1);
        data = data | 0x07;
        nim_reg_write(dev, RA0_RXADC_REG+2, &data, 1);
    }  
    NIM_MUTEX_DELETE(priv->ul_status.nim_s3501_sema);

#ifdef CHANNEL_CHANGE_ASYNC
    NIM_FLAG_DEL(priv->flag_id);
#endif

    if(OSAL_E_OK!=osal_task_delete(priv->tsk_status.m_task_id))  
    {
        AUTOSCAN_PRINTF("M3501 Task Delete Error Happenened!\n");
    }
    return SUCCESS;
}

static INT32 nim_s3501_tuner_lock(struct nim_device *dev, UINT8 *tun_lock)
{
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;

    /* Setup tuner */
    NIM_MUTEX_ENTER(priv);
    priv->nim_Tuner_Status(priv->tuner_id, tun_lock);
    NIM_MUTEX_LEAVE(priv);

    return SUCCESS;
}

/*****************************************************************************
* INT32 nim_s3501_ioctl(struct nim_device *dev, INT32 cmd, UINT32 param)
*
*  device input/output operation
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: INT32 cmd
*  Parameter3: UINT32 param
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_s3501_ioctl(struct nim_device *dev, INT32 cmd, UINT32 param)
{
    INT32 CRNum = 0;
    INT32 CURFreq = 0;
    INT32 step = 0;

    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    switch (cmd)
    {
    case NIM_DRIVER_READ_TUNER_STATUS:
        return nim_s3501_tuner_lock(dev, (UINT8 *) param);
    case NIM_DRIVER_READ_QPSK_STATUS:
        return nim_s3501_get_lock(dev, (UINT8 *) param);
    case NIM_DRIVER_READ_FEC_STATUS:
        break;
    case NIM_DRIVER_READ_QPSK_BER:
        return nim_s3501_get_BER(dev, (UINT32 *) param);
    case NIM_DRIVER_READ_VIT_BER:
        break;
    case NIM_DRIVER_READ_RSUB:
        return nim_s3501_get_PER(dev, (UINT32 *) param);
    case NIM_DRIVER_STOP_ATUOSCAN:
        priv->ul_status.s3501_autoscan_stop_flag = param;
        break;
    case NIM_DRIVER_GET_CR_NUM:
        CRNum = (INT32) nim_s3501_get_CRNum(dev);
        return CRNum;
    case NIM_DRIVER_GET_CUR_FREQ:
        switch (param)
        {
        case NIM_FREQ_RETURN_SET:
            return priv->ul_status.m_setting_freq;
        case NIM_FREQ_RETURN_REAL:
        default:
            CURFreq = (INT32) nim_s3501_get_CURFreq(dev);
            return CURFreq;
        }
    case NIM_DRIVER_FFT_PARA:
        nim_s3501_set_FFT_para(dev);
        break;
    case NIM_DRIVER_FFT:
        return nim_s3501_FFT(dev, param);
    case NIM_FFT_JUMP_STEP:
        step = 0;
        nim_s3501_get_tune_freq(dev, &step);
        return step;
    case NIM_DRIVER_SET_RESET_CALLBACK:
        priv->ul_status.m_pfn_reset_s3501 = (pfn_nim_reset_callback) param;
        break;
    case NIM_DRIVER_RESET_PRE_CHCHG:
        if (priv->ul_status.m_pfn_reset_s3501)
        {
            priv->ul_status.m_pfn_reset_s3501((priv->tuner_id + 1) << 16);
        }
        break;
    case NIM_DRIVER_ENABLE_DVBS2_HBCD:
        priv->ul_status.m_enable_dvbs2_hbcd_mode = param;
        nim_s3501_hbcd_timeout(dev, NIM_OPTR_IOCTL);
        break;
    case NIM_DRIVER_STOP_CHANSCAN:
        priv->ul_status.s3501_chanscan_stop_flag = param;
        break;
    case NIM_DRIVER_CHANGE_TS_GAP:
        return nim_change_ts_gap(dev, (UINT8)param);
    case NIM_DRIVER_SET_SSI_CLK:
        return nim_s3501_set_ssi_clk(dev,(UINT8)param);
        break;
    case NIM_DRIVER_SET_BLSCAN_MODE:
        if(0 == param)
           priv->blscan_mode = NIM_SCAN_FAST;
        else if(1 == param)
           priv->blscan_mode = NIM_SCAN_SLOW;
        break;

    case NIM_DRIVER_SET_POLAR:
        return nim_s3501_set_polar(dev, (UINT8)param);
        break;
    case NIM_DRIVER_SET_12V:
        return nim_s3501_set_12v(dev, (UINT8)param);
        break;
    case NIM_DRIVER_GET_SYM:
        return nim_s3501_get_symbol_rate(dev, (UINT32 *)param);
        break;
    case NIM_DRIVER_GET_BER:
        return nim_s3501_get_BER(dev, (UINT32 *)param);
        break;
    case NIM_DRIVER_GET_AGC:
        return nim_s3501_get_AGC(dev, (UINT8 *)param);
        break;
    case NIM_TURNER_SET_STANDBY:
     if (nim_s3501_i2c_open(dev))
        return S3501_ERR_I2C_NO_ACK;
     if (priv->nim_Tuner_Control != NULL)
     {
        priv->nim_Tuner_Control(priv->tuner_id, NIM_TUNER_SET_STANDBY_CMD, 0);
     }
     if (nim_s3501_i2c_close(dev))
        return S3501_ERR_I2C_NO_ACK;
     break;
    default:
        break;
    }
    return SUCCESS;
}

/*****************************************************************************
* INT32 nim_s3501_set_polar(struct nim_device *dev, UINT8 polar)
* Description: S3501 set polarization
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT8 pol
*
* Return Value: void
*****************************************************************************/
//#define REVERT_POLAR
static INT32 nim_s3501_set_polar(struct nim_device *dev, UINT8 polar)
{
    UINT8 data = 0;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    if (priv->ext_lnb_control)
        return priv->ext_lnb_control(priv->ext_lnb_id, LNB_CMD_SET_POLAR, (UINT32) polar);
    nim_reg_read(dev, R7C_DISEQC_CTRL, &data, 1);

    if ((priv->Tuner_Config_Data.QPSK_Config & M3501_POLAR_REVERT) == 0x00) //not exist H/V polarity revert.
    {
        if (NIM_PORLAR_HORIZONTAL == polar)
        {
            data &= 0xBF;
            NIM_PRINTF("nim_s3501_set_polar CR5C is 00\n");
        }
        else if (NIM_PORLAR_VERTICAL == polar)
        {
            data |= 0x40;
            NIM_PRINTF("nim_s3501_set_polar CR5C is 40\n");
        }
        else
        {
            NIM_PRINTF("nim_s3501_set_polar error\n");
            return ERR_FAILUE;
        }
    }
    else//exist H/V polarity revert.
    {
        if (NIM_PORLAR_HORIZONTAL == polar)
        {
            data |= 0x40;
            NIM_PRINTF("nim_s3501_set_polar CR5C is 40\n");
        }
        else if (NIM_PORLAR_VERTICAL == polar)
        {
            data &= 0xBF;
            NIM_PRINTF("nim_s3501_set_polar CR5C is 00\n");
        }
        else
        {
            NIM_PRINTF("nim_s3501_set_polar error\n");
            return ERR_FAILUE;
        }
    }

    nim_reg_write(dev, R7C_DISEQC_CTRL, &data, 1);
    return SUCCESS;
}

/*****************************************************************************
* INT32 nim_s3501_set_12v(struct nim_device *dev, UINT8 flag)
* Description: S3501 set LNB votage 12V enable or not
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT8 flag
*
* Return Value: SUCCESS
*****************************************************************************/
static INT32 nim_s3501_set_12v(struct nim_device *dev, UINT8 flag)
{
    return SUCCESS;
}

static INT32 nim_s3501_set_ssi_clk(struct nim_device *dev, UINT8 bit_rate)
{
    UINT8 data;
    if (bit_rate >= ssi_clock_tab[0])
    {
        // use 135M SSI debug
        nim_reg_read(dev, RDF_TS_OUT_DVBS2, &data, 1);
        data = (data & 0x0f) | 0xf0;
        nim_reg_write(dev, RDF_TS_OUT_DVBS2, &data, 1);

        nim_reg_read(dev, RCE_TS_FMT_CLK, &data, 1);
        data = (data & 0xf3) | 0x08; 
        nim_reg_write(dev, RCE_TS_FMT_CLK, &data, 1);
    }
    else if (bit_rate > ssi_clock_tab[1])
    {
        // use 98M SSI debug
        data = 0x1d;  
        nim_reg_write(dev, RDF_TS_OUT_DVBS2, &data, 1);
    }
    else if (bit_rate > ssi_clock_tab[2])
    {
        // use 90M SSI debug
        data = 0xfd;
        nim_reg_write(dev, RDF_TS_OUT_DVBS2, &data, 1);
        nim_reg_read(dev, RCE_TS_FMT_CLK, &data, 1);
        data = (data & 0xf3) | 0x04; 
        nim_reg_write(dev, RCE_TS_FMT_CLK, &data, 1);
    }
    else if (bit_rate > ssi_clock_tab[3])
    {
        // use 83M SSI debug
        data = 0x0d;  
        nim_reg_write(dev, RDF_TS_OUT_DVBS2, &data, 1);
    }
    else if (bit_rate > ssi_clock_tab[4])
    {
        // use 77M SSI debug
        data = 0x2d;  
        nim_reg_write(dev, RDF_TS_OUT_DVBS2, &data, 1);
    }
    else if (bit_rate > ssi_clock_tab[5])
    {
        // use 72M SSI debug
        data = 0xfd;
        nim_reg_write(dev, RDF_TS_OUT_DVBS2, &data, 1);
        nim_reg_read(dev, RCE_TS_FMT_CLK, &data, 1);
        data = (data & 0xf3) | 0x0c; 
        nim_reg_write(dev, RCE_TS_FMT_CLK, &data, 1);
    }
    else if (bit_rate > ssi_clock_tab[6])
    {
        // use 67M SSI debug
        data = 0x3d;
        nim_reg_write(dev, RDF_TS_OUT_DVBS2, &data, 1);
    }
    else
    {
        // use 60M SSI debug
        data = 0x4d;
        nim_reg_write(dev, RDF_TS_OUT_DVBS2, &data, 1);
    }
    NIM_PRINTF("clock setting is: %02x \n", data);
    return SUCCESS;
}


static INT32 nim_set_ts_rs(struct nim_device *dev, UINT32 Rs)
{
    UINT8 data;
    UINT32 temp;

    Rs=((Rs<<10)+500)/1000;   
    temp = (Rs*204+94)/188;   
    temp = temp + 1024;       

    if(temp > (45 * 1024))
        temp =  (45*1024);
    data = temp & 0xff;
    nim_reg_write(dev, RDD_TS_OUT_DVBS, &data, 1);
    data = (temp >> 8) & 0xff;
    nim_reg_write(dev, RDD_TS_OUT_DVBS + 0x01, &data, 1);

    nim_reg_read(dev, RDC_EQ_DBG_TS_CFG, &data, 1);
    data = data | 0x10;
    nim_reg_write(dev, RDC_EQ_DBG_TS_CFG, &data, 1);
    NIM_PRINTF("xxx set rs from register file........... %d \n");
    return SUCCESS;
}

#ifdef NIM_3501_FUNC_EXT
static INT32 nim_invert_moclk_phase(struct nim_device *dev)
{
    UINT8 data;
    nim_reg_read(dev, RD8_TS_OUT_SETTING, &data, 1);
    data = data ^ 0x08;
    nim_reg_write(dev, RD8_TS_OUT_SETTING, &data, 1);
    return SUCCESS;
}

static INT32 nim_open_ssi_debug(struct nim_device *dev)
{
    UINT8 data;
    nim_reg_read(dev, RD8_TS_OUT_SETTING, &data, 1);
    data = data | 0x20;
    nim_reg_write(dev, RD8_TS_OUT_SETTING, &data, 1);
    return SUCCESS;
}

static INT32 nim_close_ssi_debug(struct nim_device *dev)
{
    UINT8 data;
    nim_reg_read(dev, RD8_TS_OUT_SETTING, &data, 1);
    data = data & 0xdf;
    nim_reg_write(dev, RD8_TS_OUT_SETTING, &data, 1);
    return SUCCESS;
}

static INT32 nim_open_ts_dummy(struct nim_device *dev)
{
    UINT8 data;
    nim_reg_read(dev, RD8_TS_OUT_SETTING, &data, 1);
    data = data | 0x10;
    nim_reg_write(dev, RD8_TS_OUT_SETTING, &data, 1);
    return SUCCESS;
}
#endif

static INT32 nim_close_ts_dummy(struct nim_device *dev)
{
    UINT8 data;

    nim_reg_read(dev, RD8_TS_OUT_SETTING, &data, 1);
    data = data & 0xee;
    nim_reg_write(dev, RD8_TS_OUT_SETTING, &data, 1);
    data = 0x40;
    nim_reg_write(dev, RAD_TSOUT_SYMB + 0x01, &data, 1);
    nim_reg_read(dev, RDC_EQ_DBG_TS_CFG, &data, 1);
    data = data & 0x9f;
    nim_reg_write(dev, RDC_EQ_DBG_TS_CFG, &data, 1);
    data = 0xff;    // ssi tx debug close
    nim_reg_write(dev, RDF_TS_OUT_DVBS2, &data, 1);

    return SUCCESS;
}

#ifdef NIM_3501_FUNC_EXT
static INT32 nim_s3501_set_dmy_format(struct nim_device *dev)
{
    UINT8 data, tmp;
    //enable ECO_TS_DYM_HEAD_EN = bf[7];
    nim_reg_read(dev, RBF_S2_FEC_DBG, &data, 1);
    data = data | 0x80;
    nim_reg_write(dev, RBF_S2_FEC_DBG, &data, 1);

    //enable ECO_TS_DYM_HEAD_0 = {a2[7:3],a1[7:5]} = 47;
    nim_reg_read(dev, RA0_RXADC_REG + 0x02, &data, 1);
    tmp = TS_DYM_HEAD0 & 0xf8;
    data = (data & 0x07) | tmp;
    nim_reg_write(dev, RA0_RXADC_REG + 0x02, &data, 1);
    nim_reg_read(dev, RA0_RXADC_REG + 0x01, &data, 1);
    tmp = TS_DYM_HEAD0 & 0x07;
    tmp = (tmp << 5) & 0xff;
    data = (data & 0x1f) | tmp;
    nim_reg_write(dev, RA0_RXADC_REG + 0x01, &data, 1);

    //enable ECO_TS_DYM_HEAD_1 = {ce[7:4],a9[1:0],a8[1:0]} = 1f;
    nim_reg_read(dev, RCE_TS_FMT_CLK, &data, 1);
    tmp = TS_DYM_HEAD1 & 0xf0;
    data = (data & 0x0f) | tmp;
    nim_reg_write(dev, RCE_TS_FMT_CLK, &data, 1);
    nim_reg_read(dev, RA9_M180_CLK_DCHAN, &data, 1);
    tmp = TS_DYM_HEAD1 & 0x0c;
    tmp = (tmp >> 2) & 0xff;
    data = (data & 0xfc) | tmp;
    nim_reg_write(dev, RA9_M180_CLK_DCHAN, &data, 1);
    nim_reg_read(dev, RA8_M90_CLK_DCHAN, &data, 1);
    tmp = TS_DYM_HEAD1 & 0x03;
    data = (data & 0xfc) | tmp;
    nim_reg_write(dev, RA8_M90_CLK_DCHAN, &data, 1);

    //enable ECO_TS_DYM_HEAD_2 = {c1[7:2],cc[7:6]} = ff;
    nim_reg_read(dev, RC1_DVBS2_FEC_LDPC, &data, 1);
    tmp = TS_DYM_HEAD2 & 0xfc;
    data = (data & 0x03) | tmp;
    nim_reg_write(dev, RC1_DVBS2_FEC_LDPC, &data, 1);
    nim_reg_read(dev, RCC_STRAP_PIN_CLOCK, &data, 1);
    tmp = TS_DYM_HEAD2 & 0x03;
    tmp = (tmp << 6) & 0xff;
    data = (data & 0x3f) | tmp;
    nim_reg_write(dev, RCC_STRAP_PIN_CLOCK, &data, 1);

    //enable ECO_TS_DYM_HEAD_3 = {cf[7:1],c0[1]} = 10;
    nim_reg_read(dev, RCE_TS_FMT_CLK + 0x01, &data, 1);
    tmp = TS_DYM_HEAD3 & 0xfe;
    data = (data & 0x01) | tmp;
    nim_reg_write(dev, RCE_TS_FMT_CLK + 0x01, &data, 1);
    nim_reg_read(dev, RC0_BIST_LDPC_REG, &data, 1);
    tmp = TS_DYM_HEAD3 & 0x01;
    tmp = (tmp << 1) & 0xff;
    data = (data & 0xfd) | tmp;
    nim_reg_write(dev, RC0_BIST_LDPC_REG, &data, 1);

    //enable ECO_TS_DYM_HEAD_4 = {9f[7:4],92[7:4]} = 00;
    nim_reg_read(dev, R9C_DEMAP_BETA + 0x03, &data, 1);
    tmp = TS_DYM_HEAD4 & 0xf0;
    data = (data & 0x0f) | tmp;
    nim_reg_write(dev, R9C_DEMAP_BETA + 0x03, &data, 1);
    nim_reg_read(dev, R90_DISEQC_CLK_RATIO + 0x02, &data, 1);
    tmp = TS_DYM_HEAD4 & 0x0f;
    tmp = (tmp << 4) & 0xff;
    data = (data & 0x0f) | tmp;
    nim_reg_write(dev, R90_DISEQC_CLK_RATIO + 0x02, &data, 1);

    return SUCCESS;
}
#endif

static INT32 nim_change_ts_gap(struct nim_device *dev, UINT8 gap)
{
    UINT8 data, temp;
    if (gap == 0)
    {
        temp = 0x00;
        NIM_PRINTF("Set ts gap 0....\n");
    }
    else if (gap == 4)
    {
        temp = 1 << 1;
        NIM_PRINTF("Set ts gap 4....\n");
    }
    else if (gap == 8)
    {
        temp = 2 << 1;
        NIM_PRINTF("Set ts gap 8....\n");
    }
    else if (gap == 16)
    {
        temp = 3 << 1;
        NIM_PRINTF("Set ts gap 16....\n");
    }
    else
    {
        temp = 3 << 1;
        NIM_PRINTF("Nim error: wrong ts gap setting....\n");
    }
    nim_reg_read(dev, RD8_TS_OUT_SETTING, &data, 1);
    data = (data & 0xf9) | temp;
    nim_reg_write(dev, RD8_TS_OUT_SETTING, &data, 1);
    return SUCCESS;
}


static INT32 nim_s3501_get_bit_rate(struct nim_device *dev, UINT8 work_mode, UINT8 map_type, UINT8 code_rate, UINT32 Rs, UINT32 *bit_rate)
{
    UINT32 data;
    UINT32 temp;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;

    if (work_mode != M3501_DVBS2_MODE) // DVBS mode
    {
        if (code_rate == 0)
            temp = (Rs * 2 * 1 + 1000) / 2000;
        else if (code_rate == 1)
            temp = (Rs * 2 * 2 + 1500) / 3000;
        else if (code_rate == 2)
            temp = (Rs * 2 * 3 + 2000) / 4000;
        else if (code_rate == 3)
            temp = (Rs * 2 * 5 + 3000) / 6000;
        else
            temp = (Rs * 2 * 7 + 4000) / 8000;

        if (temp > 254)
            data = 255;
        else
            data = temp + 1; 
        *bit_rate = data;
        NIM_PRINTF("xxx dvbs bit_rate is %d \n", *bit_rate);
        return SUCCESS;
    }
    else    //DVBS2 mode
    {
        if (code_rate == 0)
            temp = (Rs * 1 + 2000) / 4000;
        else if (code_rate == 1)
            temp = (Rs * 1 + 1500) / 3000;
        else if (code_rate == 2)
            temp = (Rs * 2 + 2500) / 5000;
        else if (code_rate == 3)
            temp = (Rs * 1 + 1000) / 2000;
        else if (code_rate == 4)
            temp = (Rs * 3 + 2500) / 5000;
        else if (code_rate == 5)
            temp = (Rs * 2 + 1500) / 3000;
        else if (code_rate == 6)
            temp = (Rs * 3 + 2000) / 4000;
        else if (code_rate == 7)
            temp = (Rs * 4 + 2500) / 5000;
        else if (code_rate == 8)
            temp = (Rs * 5 + 3000) / 6000;
        else if (code_rate == 9)
            temp = (Rs * 8 + 4500) / 9000;
        else
            temp = (Rs * 9 + 5000) / 10000;

        if (map_type == 2)
            temp = temp * 2;
        else if (map_type == 3)
            temp = temp * 3;
        else if (map_type == 4)
            temp = temp * 4;
        else
        {
            NIM_PRINTF("Map type error: %02x \n", map_type);
        }

        if ((priv->Tuner_Config_Data.QPSK_Config & M3501_USE_188_MODE) == M3501_USE_188_MODE)
        {
            temp = temp;
        }
        else
        {
            temp = (temp * 204 + 94) / 188;
        }

        if (temp > 200)
            data = 200;
        else
            data = temp;
        NIM_PRINTF("Code rate is: %02x \n", code_rate);
        NIM_PRINTF("Map type is: %02x \n", map_type);

        data += 2; 
        *bit_rate = data;
        NIM_PRINTF("xxx dvbs2 bit_rate is %d \n", *bit_rate);
        return SUCCESS;
    }
}

static INT32 nim_s3501_open_ci_plus(struct nim_device *dev, UINT8 *ci_plus_flag)
{
    UINT8 data;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;

    // For CI plus test.
    if (priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_S3501D)
        data = 0x06;
    else
        data = 0x02;    // symbol period from reg, 2 cycle
    nim_reg_write(dev, RAD_TSOUT_SYMB, &data, 1);
    NIM_PRINTF("open ci plus enable REG_ad = %02x \n", data);

    nim_reg_read(dev, RAD_TSOUT_SYMB + 0x01, &data, 1);
    data = data | 0x80;    // enable symbol period from reg
    nim_reg_write(dev, RAD_TSOUT_SYMB + 0x01, &data, 1);

    nim_reg_read(dev, RDC_EQ_DBG_TS_CFG, &data, 1);
    data = data | 0xe0;
    nim_reg_write(dev, RDC_EQ_DBG_TS_CFG, &data, 1);

    nim_reg_read(dev, RDF_TS_OUT_DVBS2, &data, 1);
    data = (data & 0xfc) | 0x01;
    nim_reg_write(dev, RDF_TS_OUT_DVBS2, &data, 1);

    *ci_plus_flag = 1;

    return SUCCESS;
}

static INT32 nim_s3501_set_ts_mode(struct nim_device *dev, UINT8 work_mode, UINT8 map_type, UINT8 code_rate, UINT32 Rs,
    UINT8 channel_change_flag)
{
    UINT8 data,rdata;
    UINT32 bit_rate;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    UINT8 ci_plus_flag = 0;

    NIM_PRINTF("Enter nim_s3501_set_ts_mode:\n");

    if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
    {
        if((priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C))
        {
            if(priv->ul_status.m_tso_mode == 1)
            {
                NIM_PRINTF("C3501C use NEW TSO, but enter wrong function, nim_s3501_set_ts_mode \n");
                return SUCCESS;
            }
            else
            {
                NIM_PRINTF("C3501C use OLD TSO, nim_s3501_set_ts_mode \n");
            }
        }
        nim_reg_read(dev, RC0_BIST_LDPC_REG, &data, 1);
        data = data | 0xc0;  
        nim_reg_write(dev, RC0_BIST_LDPC_REG, &data, 1);

        bit_rate = 0x000000ff;
        nim_s3501_get_bit_rate(dev, work_mode, map_type, code_rate, Rs, &bit_rate);

        // bit rate re-caculate since all clock is over-frequency.
        bit_rate = (bit_rate * 13500 + CRYSTAL_FREQ/2) / CRYSTAL_FREQ ;

        nim_reg_read(dev, RD8_TS_OUT_SETTING, &rdata, 1);
        /****************TS output config 8bit mode begin*****************************************/
        if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_8BIT_MODE) //8bit mode
        {
            NIM_PRINTF("xxx bit rate kkk is %d \n", bit_rate);
            if (work_mode != M3501_DVBS2_MODE)// DVBS mode
            {
                if (((bit_rate >= 98) || (bit_rate <= ssi_clock_tab[8])) && channel_change_flag)
                {
                    // USE SPI dummy, no SSI debug mode.
                    if ((priv->Tuner_Config_Data.QPSK_Config & M3501_USE_188_MODE) == M3501_USE_188_MODE)
                        data = rdata & 0x08 | 0x10;
                    else
                        data = rdata & 0x08 | 0x16;
                    nim_reg_write(dev, RD8_TS_OUT_SETTING, &data, 1);
                    NIM_PRINTF("DVBS USE SPI normal dummy, no SSI debug mode \n");
                }
                else
                {
                    // USE SSI debug mode with dummy enable.
                    nim_reg_read(dev, RDC_EQ_DBG_TS_CFG, &data, 1);
                    data = data | 0x20;
                    nim_reg_write(dev, RDC_EQ_DBG_TS_CFG, &data, 1);
                    nim_s3501_set_ssi_clk(dev, (UINT8)bit_rate);
                    if ((priv->Tuner_Config_Data.QPSK_Config & M3501_USE_188_MODE) == M3501_USE_188_MODE)
                        data = rdata & 0x08 | 0x21;
                    else
                        data = rdata & 0x08 | 0x27;
                    nim_reg_write(dev, RD8_TS_OUT_SETTING, &data, 1);
                    NIM_PRINTF("DVBS  USE SSI debug mode \n");
                }
            }
            else    // DVBS2 mode
            {
                if (((bit_rate >= 98) || (bit_rate <= ssi_clock_tab[8])) && channel_change_flag)
                {
                    // USE normal SPI
                    if ((priv->Tuner_Config_Data.QPSK_Config & M3501_USE_188_MODE) == M3501_USE_188_MODE)
                        data = rdata & 0x08 | 0x10;
                    else
                        data = rdata & 0x08 | 0x16;
                    nim_reg_write(dev, RD8_TS_OUT_SETTING, &data, 1);

                    NIM_PRINTF("DVBS2 Enter normal SPI mode, not use SSI debug..\n");
                }
                else
                {
                    //use ssi debug to output 8bit spi mode
                    nim_s3501_open_ci_plus(dev, &ci_plus_flag);
                    nim_s3501_set_ssi_clk(dev, (UINT8)bit_rate);
                    if ((priv->Tuner_Config_Data.QPSK_Config & M3501_USE_188_MODE) == M3501_USE_188_MODE)
                        data = rdata & 0x08 | 0x21;
                    else
                        data = rdata & 0x08 | 0x27;
                    nim_reg_write(dev, RD8_TS_OUT_SETTING, &data, 1);
                    NIM_PRINTF("DVBS2 Enter SSI debug..\n");
                }
            }
        }
        /****************TS output config 8bit mode end*****************************************/
        /****************TS output config 1bit mode begin*****************************************/
        else if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_1BIT_MODE)    //SSI mode
        {
            //SSI mode
            if (priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_S3501D)
            {
                data = 0x60;
                nim_reg_write(dev, RAD_TSOUT_SYMB + 0x01, &data, 1);
            }
            nim_s3501_set_ssi_clk(dev, (UINT8)bit_rate);
            if (work_mode != M3501_DVBS2_MODE)
            {
                //DVBS mode
                nim_reg_read(dev, RDC_EQ_DBG_TS_CFG, &data, 1);
                data = data | 0x20;                             //add for ssi_clk change point
                nim_reg_write(dev, RDC_EQ_DBG_TS_CFG, &data, 1);
                NIM_PRINTF("DVBS USE SSI mode \n");
            }
            else
            {
                //  DVBS2 mode
                if (bit_rate < 50)
                {
                    NIM_PRINTF("Low bit rate, Close CI plus......................... \n");
                }
                else
                {
                    nim_s3501_open_ci_plus(dev, &ci_plus_flag);
                }
                NIM_PRINTF("DVBS2 USE SSI mode \n");
            }

            if ((priv->Tuner_Config_Data.QPSK_Config & M3501_USE_188_MODE) == M3501_USE_188_MODE)
                data = rdata & 0x08 | 0x01;
            else
                data = rdata & 0x08 | 0x07;    // enable SSI debug
            nim_reg_write(dev, RD8_TS_OUT_SETTING, &data, 1);
        }
        /****************TS output config 1bit mode end*****************************************/
        /****************TS output config 2bit mode begin*****************************************/
        else if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_2BIT_MODE)    //TS 2bit mode
        {
            //TS 2bit mode
            if (priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_S3501D)
            {
                if ((priv->Tuner_Config_Data.QPSK_Config & M3501_USE_188_MODE) == M3501_USE_188_MODE)
                    data = rdata & 0x08 | 0x10;
                else
                    data = rdata & 0x08 | 0x16;
                nim_reg_write(dev, RD8_TS_OUT_SETTING, &data, 1);
                data = 0x00;
                nim_reg_write(dev, RAD_TSOUT_SYMB + 0x01, &data, 1);
                NIM_PRINTF("S3501D Enter 2bit Mode============= \n");
            }
            else
            {
                //TS 2bit mode
                //ECO_SSI_2B_EN = cr9f[3]
                nim_reg_read(dev, R9C_DEMAP_BETA + 0x03, &data, 1);
                data = data | 0x08;
                nim_reg_write(dev, R9C_DEMAP_BETA + 0x03, &data, 1);

                //ECO_SSI_SEL_2B = crc0[3]
                nim_reg_read(dev, RC0_BIST_LDPC_REG, &data, 1);
                data = data | 0x08; //for 2bit mode
                nim_reg_write(dev, RC0_BIST_LDPC_REG, &data, 1);

                nim_s3501_set_ssi_clk(dev, (UINT8)bit_rate);

                if (work_mode != M3501_DVBS2_MODE)// DVBS mode
                {
                    nim_reg_read(dev, RDC_EQ_DBG_TS_CFG, &data, 1);
                    data = data | 0x20;                              //add for ssi_clk change point
                    nim_reg_write(dev, RDC_EQ_DBG_TS_CFG, &data, 1);
                    NIM_PRINTF("Enter DVBS 2bit mode \n");
                }
                else    // DVBS2 mode
                {
                    // For CI plus test.
                    if (bit_rate < 50)
                    {
                        NIM_PRINTF("Low bit rate, Close CI plus......................... \n");
                    }
                    else
                    {
                        nim_s3501_open_ci_plus(dev, &ci_plus_flag);
                    }
                    NIM_PRINTF("Enter DVBS2 2bit mode..\n");
                }

                //no matter bit_rate all use ssi_debug mode
                if ((priv->Tuner_Config_Data.QPSK_Config & M3501_USE_188_MODE) == M3501_USE_188_MODE)
                    data = rdata & 0x08 | 0x21;
                else
                    data = rdata & 0x08 | 0x27;    // enable SSI debug
                nim_reg_write(dev, RD8_TS_OUT_SETTING, &data, 1);
            }//end if M3501B 2bit
        }
        /****************TS output config 2bit mode end*****************************************/
        /****************TS output config 4bit mode begin*****************************************/
        else if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_4BIT_MODE)    //4//4bit mode
        {
            //TS 4bit mode
            if (priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_S3501D)
            {
                if ((priv->Tuner_Config_Data.QPSK_Config & M3501_USE_188_MODE) == M3501_USE_188_MODE)
                    data = rdata & 0x08 | 0x10;
                else
                    data = rdata & 0x08 | 0x16;    //moclk interv for 4bit.
                nim_reg_write(dev, RD8_TS_OUT_SETTING, &data, 1);
                data = 0x20;
                nim_reg_write(dev, RAD_TSOUT_SYMB + 0x01, &data, 1);
                NIM_PRINTF("S3501D Enter 4bit Mode============= \n");
            }
            else
            {
                //TS 4bit mode
                //ECO_SSI_2B_EN = cr9f[3]
                nim_reg_read(dev, R9C_DEMAP_BETA + 0x03, &data, 1);
                data = data | 0x08;
                nim_reg_write(dev, R9C_DEMAP_BETA + 0x03, &data, 1);

                //ECO_SSI_SEL_2B = crc0[3]
                nim_reg_read(dev, RC0_BIST_LDPC_REG, &data, 1);
                data = data & 0xf7; //for 4bit mode
                nim_reg_write(dev, RC0_BIST_LDPC_REG, &data, 1);

                nim_s3501_set_ssi_clk(dev, (UINT8)bit_rate);

                if (work_mode != M3501_DVBS2_MODE)// DVBS mode
                {
                    nim_reg_read(dev, RDC_EQ_DBG_TS_CFG, &data, 1);
                    data = data | 0x20;                                                     //add for ssi_clk change point
                    nim_reg_write(dev, RDC_EQ_DBG_TS_CFG, &data, 1);
                    NIM_PRINTF("M3501B Enter DVBS 4bit mode \n");
                }
                else    // DVBS2 mode
                {
                    // For CI plus test.
                    if (bit_rate < 50)
                    {
                        NIM_PRINTF("Low bit rate, Close CI plus......................... \n");
                    }
                    else
                    {
                        nim_s3501_open_ci_plus(dev, &ci_plus_flag);
                    }
                    NIM_PRINTF("M3501B Enter DVBS2 4bit mode..\n");
                }

                //no matter bit_rate all use ssi_debug mode
                if ((priv->Tuner_Config_Data.QPSK_Config & M3501_USE_188_MODE) == M3501_USE_188_MODE)
                    data = rdata & 0x08 | 0x21;
                else
                    data = rdata & 0x08 | 0x27;    // enable SSI debug
                nim_reg_write(dev, RD8_TS_OUT_SETTING, &data, 1);
            }    //end if M3501B  4bit
        }
        if (ci_plus_flag)
        {
            //RST fsm
            data = 0x91;
            nim_reg_write(dev, R00_CTRL, &data, 1);
            data = 0x51;
            nim_reg_write(dev, R00_CTRL, &data, 1);
        }
        /****************TS output config 4bit mode end*****************************************/
        //end M3501B config.
    }
    else
    {
        //M3501A
        if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_1BIT_MODE)
            data = 0x60;
        else if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_2BIT_MODE)
            data = 0x00;
        else if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_4BIT_MODE)
            data = 0x20;
        else if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_8BIT_MODE)
            data = 0x40;
        else
            data = 0x40;
        nim_reg_write(dev, RAD_TSOUT_SYMB + 0x01, &data, 1);

        nim_reg_read(dev, RAD_TSOUT_SYMB + 0x01, &rdata, 1);
        if(rdata != data)
            NIM_PRINTF("M3501A set TS mode faily !(%0x , %0x )\n",data,rdata);
        else
        {
            NIM_PRINTF("M3501A set TS mode successfully !( %0x )\n",data);
        }

    }

    NIM_PRINTF("Leave nim_s3501_set_ts_mode\n");

    return SUCCESS;
}

#ifdef NIM_3501_FUNC_EXT
static INT32 nim_s3501_get_phase_error(struct nim_device *dev, INT32 *phase_error)
{
    UINT8 rdata = 0;
    UINT8 data = 0;

    nim_reg_read(dev, RC0_BIST_LDPC_REG + 4, &data, 1);
    if (data & 0x80)
    {
        data &= 0x7f;
        nim_reg_write(dev, RC0_BIST_LDPC_REG + 4, &data, 1);
    }
    nim_reg_read(dev, RC0_BIST_LDPC_REG, &data, 1);
    if ((data & 0x04) == 0)
    {
        nim_reg_read(dev, RC0_BIST_LDPC_REG + 5, &rdata, 1);
        data |= 0x04;
        nim_reg_write(dev, RC0_BIST_LDPC_REG, &data, 1);
        if (rdata & 0x80)
            *phase_error = rdata - 256;
        else
            *phase_error = rdata; 
        NIM_PRINTF("phase error is %d\n", (*phase_error));
        return SUCCESS;  
    }
    else
    {
        *phase_error = 0;
        return ERR_FAILUE;  
    }
}
#endif

static INT32 nim_s3501_set_phase_noise(struct nim_device *dev)
{
    UINT32 debug_time, debug_time_thre, i;
    UINT8 snr, data, verdata, sdat;
    UINT32 ber, per;
    UINT32 min_per, max_per;
    UINT32 per_buf[4] =
    {
        0, 0, 0, 0
    };   
    UINT32 buf_index = 0;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    NIM_PRINTF("            Eenter function set_phase_noise\n");
    sdat = 0xba;
    nim_reg_write(dev, RB5_CR_PRS_TRA, &sdat, 1);
    debug_time = 0;
    debug_time_thre = 4;
    sdat = 0xba;

    data = 0x00;
    nim_reg_write(dev, R74_PKT_STA_NUM, &data, 1);
    data = 0x10;    
    nim_reg_write(dev, R74_PKT_STA_NUM + 0x01, &data, 1);
    for (debug_time = 0; debug_time < debug_time_thre; debug_time++)
    {
        nim_reg_read(dev, R04_STATUS, &data, 1);
        if ((data & 0x3f) == 0x3f)
        {
            break;
        }
        data = 0x80;
        nim_reg_write(dev, R76_BIT_ERR + 0x02, &data, 1);
        nim_reg_read(dev, R76_BIT_ERR + 0x02, &verdata, 1);
        if (verdata != data)
        {
            NIM_PRINTF("!!! RESET BER ERROR!\n");
        }
        data = 0x80;
        nim_reg_write(dev, R79_PKT_ERR + 0x01, &data, 1);
        nim_reg_read(dev, R79_PKT_ERR + 0x01, &verdata, 1);
        if (verdata != data)
        {
            NIM_PRINTF("!!! RESET PER ERROR!\n");
        }
        comm_delay(100);

        nim_s3501_get_SNR(dev, &snr);
        nim_s3501_get_new_BER(dev, &ber);
        nim_s3501_get_new_PER(dev, &per);
        NIM_PRINTF("--- snr/ber/per = %x/%x/%x\n", snr, ber, per);

        per_buf[buf_index] = per;
        sdat = sdat - 0x10;
        buf_index ++;
        nim_reg_write(dev, RB5_CR_PRS_TRA, &sdat, 1);
        if ((per_buf[buf_index - 2] == 0) && (buf_index >= 2))
            break;
    }



    min_per = 0;
    max_per = 0;
    for (i = 0; i < buf_index; i++)
    {
        NIM_PRINTF("per_buf[%d] = 0x%x\n", i, per_buf[i]);
        per_buf[i] >>= 4;
        if (per_buf[i] < per_buf[min_per])
            min_per = i;
        if (per_buf[i] > per_buf[max_per])
        {
            max_per = i;
            if (i > 1)
            {
                break;  
            }
        }
    }

    if (min_per <= max_per)
    {
        priv->t_Param.t_phase_noise_detected = 0;
        NIM_PRINTF("No phase noise detected!\n");
    }
    else
    {
        priv->t_Param.t_phase_noise_detected = 1;
        NIM_PRINTF("Phase noise detected!\n");
        data = 0x42;
        nim_reg_write(dev, R33_CR_CTRL + 0x04, &data, 1);
    }

    NIM_PRINTF("min_per is %d, max_per is %d\n", min_per, max_per);
    if ((min_per < buf_index - 1) && (per_buf[min_per] == per_buf[min_per + 1]))
    {
        if ((per_buf[min_per] > 0) || (snr < 0x10) || (priv->t_Param.t_phase_noise_detected == 0))
            sdat = 0xba - min_per * 0x10;
        else
        {
            sdat = 0xaa - min_per * 0x10;
        }
        nim_reg_write(dev, RB5_CR_PRS_TRA, &sdat, 1);
    }
    else
    {
        sdat = 0xba - min_per * 0x10;
        nim_reg_write(dev, RB5_CR_PRS_TRA, &sdat, 1);
    }

    NIM_PRINTF("--------------------EXIT set_phase_noise, REG_b5 = 0x%x\n", sdat);

    data = 0x10;
    nim_reg_write(dev, R74_PKT_STA_NUM, &data, 1);
    data = 0x27;
    nim_reg_write(dev, R74_PKT_STA_NUM + 0x01, &data, 1);
    return SUCCESS;
}


#define sys_ms_cnt (SYS_CPU_CLOCK/2000)

static INT32 nim_s3501_waiting_channel_lock(struct nim_device *dev, UINT32 freq, UINT32 sym)
{
    UINT32 timeout, locktimes = 200;
    UINT32 tempFreq;
    UINT32 Rs;
    UINT32 starttime = 0;
    UINT32 endtime;
    UINT8 work_mode, map_type, iqswap_flag, roll_off, modcod;
    UINT8 intindex, intdata;
    UINT8 code_rate;
    UINT8 data;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    UINT8 channel_change_flag = 1; 
    timeout = 0;

    NIM_PRINTF("Enter nim_s3501_waiting_channel_lock : freq=%d, sym=%d\n",freq,sym);

    if (sym > 40000)
        locktimes = 204;
    else if (sym < 2000)
        locktimes = 2000;
    else if (sym < 4000)
        locktimes = 1604 - sym / 40;
    else if (sym < 6500)
        locktimes = 1004 - sym / 60;
    else
        locktimes = 604 - sym / 100;

    if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
        locktimes *= 3;
    else
        locktimes *= 2;

    while (priv->ul_status.s3501_chanscan_stop_flag == 0)
    {
        timeout ++ ;
        if (locktimes < timeout)                    
        {
            priv->t_Param.phase_noise_detect_finish = 1;
            nim_s3501_clear_int(dev);
            NIM_PRINTF("    timeout \n");
            priv->ul_status.s3501_chanscan_stop_flag = 0;
            if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
                priv->tsk_status.m_lock_flag = NIM_LOCK_STUS_SETTING;
            return ERR_FAILED;
        }

        nim_reg_read(dev, R04_STATUS, &intdata, 1);
        if (0x3f == (intdata & 0x3f))
        {
            nim_s3501_reg_get_work_mode(dev, &work_mode);
            switch(work_mode)
            {
                case 0 : {NIM_PRINTF("            work_mode is DVB-S \n"); break;}
                case 1 : {NIM_PRINTF("            work_mode is DVB-S2 \n"); break;}
                case 2 : {NIM_PRINTF("            work_mode is DVB-H8PSK \n"); break;}
            }
            if((priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B) && (priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C))
            {
                if((priv->ul_status.m_tso_mode == 1))
                {
                    if(work_mode == M3501_DVBS2_MODE)
                    {
                        nim_m3501c_open_dummy(dev);
                    }
                    else
                    {
                        nim_m3501c_close_dummy(dev); 
                    }
#ifdef C3501C_ERRJ_LOCK
                    nim_m3501c_recover_moerrj(dev);
#endif
                    priv->tsk_status.m_lock_flag = NIM_LOCK_STUS_CLEAR;
                    priv->ul_status.m_CurFreq = tempFreq;
                    priv->ul_status.s3501_chanscan_stop_flag = 0;
                }
            }


            // ------ Get information start
            tempFreq = freq;
            nim_s3501_reg_get_freq(dev, &tempFreq);
            NIM_PRINTF("            Freq is %d\n", (LNB_LOACL_FREQ - tempFreq));

            nim_s3501_reg_get_symbol_rate(dev, &Rs);
            NIM_PRINTF("            Rs is %d\n", Rs);

            nim_s3501_reg_get_code_rate(dev, &code_rate);
            if(0==work_mode)
            {
                switch(code_rate)
                {
                    case 0 : {NIM_PRINTF("            code_rate is 1/2\n"); break;}
                    case 1 : {NIM_PRINTF("            code_rate is 2/3\n"); break;}
                    case 2 : {NIM_PRINTF("            code_rate is 3/4\n"); break;}
                    case 3 : {NIM_PRINTF("            code_rate is 5/6\n"); break;}
                    case 4 : {NIM_PRINTF("            code_rate wrong\n"); break;}
                    case 5 : {NIM_PRINTF("            code_rate is 7/8\n"); break;}
                }

            }
            else if(1==work_mode)
            {
                switch(code_rate)
                {
                    case 0 : {NIM_PRINTF("            code_rate is 1/4\n"); break;}
                    case 1 : {NIM_PRINTF("            code_rate is 1/3\n"); break;}
                    case 2 : {NIM_PRINTF("            code_rate is 2/5\n"); break;}
                    case 3 : {NIM_PRINTF("            code_rate is 1/2\n"); break;}
                    case 4 : {NIM_PRINTF("            code_rate is 3/5\n"); break;}
                    case 5 : {NIM_PRINTF("            code_rate is 2/3\n"); break;}
                    case 6 : {NIM_PRINTF("            code_rate is 3/4\n"); break;}
                    case 7 : {NIM_PRINTF("            code_rate is 4/5\n"); break;}
                    case 8 : {NIM_PRINTF("            code_rate is 5/6\n"); break;}
                    case 9 : {NIM_PRINTF("            code_rate is 8/9\n"); break;}
                    case 10 : {NIM_PRINTF("            code_rate is 9/10\n"); break;}
                }

            }

            if(1==work_mode)
            {
                nim_s3501_reg_get_roll_off(dev, &roll_off);
                switch(roll_off)
                {
                    case 0 : {NIM_PRINTF("            roll_off is 0.35\n");break;};
                    case 1 : {NIM_PRINTF("            roll_off is 0.25\n");break;};
                    case 2 : {NIM_PRINTF("            roll_off is 0.20\n");break;};
                    case 3 : {NIM_PRINTF("            roll_off is wrong\n");break;};
                }
            }
            if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
            {
                if(1==work_mode)
                {
                    if(priv ->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C)
                    {
                        nim_s3501_reg_get_modcod(dev, &modcod);
                        if(modcod & 0x01)
                            NIM_PRINTF("            Pilot on \n");
                        else
                        {
                            NIM_PRINTF("            Pilot off \n");
                        }
                        modcod = modcod>>2;
                        NIM_PRINTF("            Modcod is %d\n", modcod);
                    }
                }
            }
            nim_s3501_reg_get_iqswap_flag(dev, &iqswap_flag);
            NIM_PRINTF("            iqswap_flag is %d\n", iqswap_flag);

            nim_s3501_reg_get_map_type(dev, &map_type);
            if(1==work_mode)
            {
                switch(map_type)
                {
                    case 0 : {NIM_PRINTF("            map_type is HBCD\n"); break;}
                    case 1 : {NIM_PRINTF("            map_type is BPSK\n"); break;}
                    case 2 : {NIM_PRINTF("            map_type is QPSK\n"); break;}
                    case 3 : {NIM_PRINTF("            map_type is 8PSK\n"); break;}
                    case 4 : {NIM_PRINTF("            map_type is 16APSK\n"); break;}
                    case 5 : {NIM_PRINTF("            map_type is 32APSK\n"); break;}
                }
            }
            else
            {
                NIM_PRINTF("            map_type is QPSK\n");
            }

            // ------ Get information end

            if ((priv->ul_status.m_enable_dvbs2_hbcd_mode == 0) && ((map_type == 0) || (map_type == 5)))
            {
                NIM_PRINTF("            Demod Error: wrong map_type is %d\n", map_type);
            }
            else
            {
                NIM_PRINTF("        lock chanel \n");

                #ifdef NIM_S3501_ASCAN_TOOLS
                    tmp_lock=0x01;
                #endif

                if (work_mode == M3501_DVBS2_MODE)
                {
                    data = 0x52;
                    nim_reg_write(dev, R33_CR_CTRL + 0x04, &data, 1);
                    data = 0xba;
                    nim_reg_write(dev, RB5_CR_PRS_TRA, &data, 1);
                }      

                if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
                {
                    if (work_mode != M3501_DVBS2_MODE)
                    {
                        if(CRYSTAL_FREQ > 15000)
                            data = 0xf7;
                        else
                            data = 0x77;
                        nim_reg_write(dev, R5B_ACQ_WORK_MODE, &data, 1);
                        priv->ul_status.phase_err_check_status = 1000;
                    }

                    if((priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501B) ||
                       ((priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C) && (priv->ul_status.m_tso_mode == 0)))
                    {
                        priv->tsk_status.m_lock_flag = NIM_LOCK_STUS_CLEAR;
                        nim_s3501_set_ts_mode(dev, work_mode, map_type, code_rate, Rs, channel_change_flag);
			   nim_m3501c_fec_ts_on(dev);
			   nim_m3501_ts_on(dev);
			   
                        nim_reg_read(dev, R04_STATUS, &data, 1);
                        if (0x3f == data & 0x3f)
                        {
                            priv->ul_status.s3501d_lock_status = NIM_LOCK_STUS_NORMAL;  
                            if ((work_mode == M3501_DVBS2_MODE) && (map_type == 3))
                                nim_s3501_set_phase_noise(dev);
                        }
                        else
                        {
                            priv->ul_status.s3501d_lock_status = NIM_LOCK_STUS_SETTING;  
                        }
                    }
                }
                else
                {
                    //M3501A
                    if (work_mode != M3501_DVBS2_MODE)
                    {
                        if(CRYSTAL_FREQ > 15000)
                            data = 0xf7;
                        else
                            data = 0x77;
                        nim_reg_write(dev, R5B_ACQ_WORK_MODE, &data, 1);
                    }
                    else
                    {
                        if (map_type == 3)
                        {
                            // S2,8PSK
                            nim_s3501_set_phase_noise(dev);
                        }
                    }
                }

                priv->t_Param.phase_noise_detect_finish = 1;
                if ((work_mode == M3501_DVBS2_MODE) && (map_type == 3) && (priv->t_Param.t_phase_noise_detected == 0))
                {
                    // S2, 8PSK
                    if (code_rate == 4)
                    {
                        // coderate3/5
                        priv->t_Param.t_snr_thre1 = 30;
                        priv->t_Param.t_snr_thre2 = 45;
                        priv->t_Param.t_snr_thre3 = 85;
                    }
                    else if ((code_rate == 5) || (code_rate == 6))
                    {
                        // coderate2/3,3/4
                        priv->t_Param.t_snr_thre1 = 35;
                        priv->t_Param.t_snr_thre2 = 55;
                    }
                    else if (code_rate == 7)
                    {
                        // coderate5/6
                        priv->t_Param.t_snr_thre1 = 55;
                        priv->t_Param.t_snr_thre2 = 65;
                    }
                    else if (code_rate == 8)
                    {
                        // coderate8/9
                        priv->t_Param.t_snr_thre1 = 75;
                    }
                    else
                    {
                        // coderate9/10
                        priv->t_Param.t_snr_thre1 = 80;
                    }
                }

                if ((work_mode == M3501_DVBS2_MODE) && (map_type <= 3)) //only s2 need dynamic power
                    priv->t_Param.t_dynamic_power_en = 1;

                /* Keep current frequency.*/
                priv->ul_status.m_CurFreq = tempFreq;
                nim_s3501_clear_int(dev);
                priv->ul_status.s3501_chanscan_stop_flag = 0;
                return SUCCESS;
            }
        }
        else
        {
            if (priv->ul_status.s3501_chanscan_stop_flag)
            {
                priv->ul_status.s3501_chanscan_stop_flag = 0;
                if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
                    priv->tsk_status.m_lock_flag = NIM_LOCK_STUS_SETTING;
                return ERR_FAILED;
            }
            comm_delay(200);
        }
    }

    priv->ul_status.s3501_chanscan_stop_flag = 0;
    return SUCCESS;
}

static INT32 nim_s3501_channel_change(struct nim_device *dev, UINT32 freq, UINT32 sym, UINT8 fec)
{
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    UINT8 data = 0x10;
    UINT8 lock = 200;
    UINT8 low_sym;
    UINT8 channel_change_flag = 1; 
    UINT8 TabID;
    UINT16 TabVal,TabValTemp;
    UINT8 DatArray[2];

    NIM_PRINTF("Enter nim_s3501_channel_change : freq=%d, sym=%d, fec=%d \n",freq,sym,fec);

    priv->t_Param.t_phase_noise_detected = 0;
    priv->t_Param.t_dynamic_power_en = 0;
    priv->t_Param.t_last_snr = -1;
    priv->t_Param.t_last_iter = -1;
    priv->t_Param.t_aver_snr = -1;
    priv->t_Param.t_snr_state = 0;
    priv->t_Param.t_snr_thre1 = 256;
    priv->t_Param.t_snr_thre2 = 256;
    priv->t_Param.t_snr_thre3 = 256;
    priv->t_Param.phase_noise_detect_finish = 0x00;
    priv->ul_status.m_tso_status = NIM_TSO_STUS_UNLOCK;
	
#ifdef CHANNEL_CHANGE_ASYNC
    UINT32 flag_ptn = 0;
    if (NIM_FLAG_WAIT(&flag_ptn, priv->flag_id, NIM_FLAG_CHN_CHG_START | NIM_FLAG_CHN_CHANGING, OSAL_TWF_ORW, 0) == OSAL_E_OK)
    {
        // channel chaning, stop the old changing first.
        priv->ul_status.s3501_chanscan_stop_flag = 1;
        comm_sleep(2); 
    }
#endif

    if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
    {
        priv->ul_status.phase_err_check_status = 0;
        priv->ul_status.s3501d_lock_status = NIM_LOCK_STUS_NORMAL;
    }

    priv->ul_status.m_setting_freq = freq;

    //reset
    nim_s3501_demod_ctrl(dev, NIM_DEMOD_CTRL_0X91);

    if(15000>=CRYSTAL_FREQ)
    {
        //if(sym<44000)
        if(sym<(CRYSTAL_FREQ*440/135))
            nim_s3501_set_dsp_clk (dev, 0);
        else
            nim_s3501_set_dsp_clk (dev, 3);
    }
    else
    {
        nim_s3501_set_dsp_clk (dev, 0);
    }

    if ((0 == freq) || (0 == sym))
        return SUCCESS;

    nim_s3501_sym_config(dev, sym);

#if 0
    if (priv->ul_status.s3501_chanscan_stop_flag)
    {
        priv->ul_status.s3501_chanscan_stop_flag = 0;
        return SUCCESS;
    }
#endif

    nim_s3501_TR_CR_Setting(dev, NIM_OPTR_CHL_CHANGE);

    low_sym = sym < 6500 ? 1 : 0;   /* Symbol rate is less than 10M, low symbol rate */

    nim_s3501_freq_offset_set(dev, low_sym, &freq);

    if (nim_s3501_i2c_open(dev))
        return S3501_ERR_I2C_NO_ACK;

    if (priv->nim_Tuner_Control != NULL)
    {
        priv->nim_Tuner_Control(priv->tuner_id, freq, sym);
    }

    lock = 200;
    comm_sleep(1);
    if (priv->nim_Tuner_Status != NULL)
        priv->nim_Tuner_Status(priv->tuner_id, &lock);

    if (nim_s3501_i2c_close(dev))
        return S3501_ERR_I2C_NO_ACK;


    data = 0x10;
    nim_reg_write(dev,RB3_PIN_SHARE_CTRL, &data, 1);
    nim_s3501_adc_setting(dev);

    nim_s3501_interrupt_mask_clean(dev);

    nim_s3501_set_hw_timeout(dev, 0xff);

    nim_s3501_agc1_ctrl(dev, low_sym, NIM_OPTR_CHL_CHANGE);

    nim_s3501_set_RS(dev, sym);

    nim_s3501_freq_offset_reset(dev, low_sym);

    nim_s3501_cr_setting(dev, NIM_OPTR_CHL_CHANGE);

    nim_s3501_set_acq_workmode(dev, NIM_OPTR_CHL_CHANGE0);

    nim_s3501_set_FC_Search_Range(dev, NIM_OPTR_CHL_CHANGE, sym);
    nim_s3501_RS_Search_Range(dev, NIM_OPTR_CHL_CHANGE, sym);

    nim_s3501_ldpc_setting(dev, NIM_OPTR_CHL_CHANGE, 0x00, 0x01);

    nim_s3501_hbcd_timeout(dev, NIM_OPTR_CHL_CHANGE);

    if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
    {
        if((priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C) && (priv->ul_status.m_tso_mode == 1))
        {
#ifdef C3501C_ERRJ_LOCK
            nim_m3501c_invert_moerrj(dev);
#endif
            nim_m3501c_fec_ts_off(dev);
	     nim_m3501c_reset_tso(dev);
        }
        else
        {
            nim_m3501c_fec_ts_off(dev);
            nim_set_ts_rs(dev, sym);
            //when enter channel change, first close ts dummy.
            nim_close_ts_dummy(dev);
            //ECO_TS_EN = reg_cr9e[7], disable before dmy config successfully.
	     nim_m3501_ts_off(dev);
        }
    }
    else if(priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501A)
    {
        //M3501A config.
        nim_s3501_set_ts_mode(dev, 0x0, 0x0, 0x0, 0x0, 0X1);
    }
    else
    {
        NIM_PRINTF("ERROR: CHIP_ID_type=0x%0x \n",priv->ul_status.m_s3501_type);
    }

    comm_delay(10);

    if (sym < 3000)
    {
        if (sym < 2000)
            data = 0x08;
        else
            data = 0x0a;
        nim_reg_write(dev, R1B_TR_TIMEOUT_BAND, &data, 1);
    }

    #ifdef SW_ADPT_CR
    if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B && priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C)
    {
        snr_initial_en = 1;
    }
    #endif 

    #ifdef HW_ADPT_CR
    if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B && priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C)
    {
        data = 0xe1; 
        nim_reg_write(dev, R0E_ADPT_CR_CTRL, &data, 1);

        data = 0xf0 | 2; 
        nim_reg_write(dev, R9D_RPT_DEMAP_BETA, &data, 1);
        if((sym>3000)&&(sym<=10000))//add(sym<=3000)for 1M QPSK AWGN performance issue 
            DatArray[0] = 0x02;
        else
            DatArray[0] = 0x00;
        DatArray[1] = 0x10;
        nim_reg_write(dev, R11_DCC_OF_I, DatArray, 2);

		data = 0xf0 | 3; 
		nim_reg_write(dev, R9D_RPT_DEMAP_BETA, &data, 1);
		if(sym<=1800) 
			TabVal = 0x3F8; 
		else
			TabVal = 0x1F8; 
		DatArray[0] = (UINT8)(TabVal & 0x0ff);
		DatArray[1] = (UINT8)(((TabVal>>8) & 0x0ff) | 0x10);
		nim_reg_write(dev, R11_DCC_OF_I, DatArray, 2);
		
        if(sym<=5000)
            TabVal = 0x1280;
        else
               TabVal = 0x1290;

        TabValTemp = (TabVal & 3)<<9;
        TabVal = (TabVal>>4) | TabValTemp;// to HW format
        DatArray[0] = (UINT8)(TabVal & 0x0ff);
        DatArray[1] = (UINT8)(((TabVal>>8) & 0x0ff) | 0x10);

        for(TabID=2; TabID<=7; TabID++) 
        {
            data = (TabID<<4) | (TAB_SIZE-1);
            nim_reg_write(dev, R9D_RPT_DEMAP_BETA, &data, 1);
            nim_reg_write(dev, R11_DCC_OF_I, DatArray, 2);

            data = (TabID<<4) | (TAB_SIZE-2);
            nim_reg_write(dev, R9D_RPT_DEMAP_BETA, &data, 1);
            nim_reg_write(dev, R11_DCC_OF_I, DatArray, 2);

            data = (TabID<<4) | (TAB_SIZE-3);
            nim_reg_write(dev, R9D_RPT_DEMAP_BETA, &data, 1);
            nim_reg_write(dev, R11_DCC_OF_I, DatArray, 2);
        }

        #ifdef HW_ADPT_CR_MONITOR
            data = 0xe6; // enable hw adpt CR report, also enable always update to regfile, latter need review
        #else
            data = 0xe0; // CR Tab init off
        #endif

        nim_reg_write(dev, R0E_ADPT_CR_CTRL, &data, 1);
    }
    #endif 


    nim_s3501_demod_ctrl(dev, NIM_DEMOD_CTRL_0X51);

#if defined CHANNEL_CHANGE_ASYNC && !defined NIM_S3501_ASCAN_TOOLS
    priv->cur_freq = freq;
    priv->cur_sym = sym;
    NIM_FLAG_SET(priv->flag_id, NIM_FLAG_CHN_CHG_START);
#else
    nim_s3501_waiting_channel_lock(dev, freq, sym);
#endif

    priv->ul_status.s3501_chanscan_stop_flag = 0;
    NIM_PRINTF("Leave nim_s3501_channel_change\n");
    
    return SUCCESS;
}

void nim_s3501_clear_int(struct nim_device *dev)
{
    UINT8 data;
    UINT8 rdata;

    data = 0x00;
    nim_reg_write(dev, R02_IERR, &data, 1);

    nim_reg_read(dev, R00_CTRL, &rdata, 1);
    data = (rdata | 0x10);
    nim_s3501_demod_ctrl(dev, data);
}

UINT32 nim_s3501_get_CURFreq(struct nim_device *dev)
{
    return ((struct nim_s3501_private *) dev->priv)->ul_status.m_CurFreq;
}
UINT8 nim_s3501_get_CRNum(struct nim_device *dev)
{
    return ((struct nim_s3501_private *) (dev->priv))->ul_status.m_CRNum;
}

void nim_s3501_set_FFT_para(struct nim_device *dev)
{
    return;
}

INT32 nim_s3501_FFT(struct nim_device *dev, UINT32 startFreq)
{
    UINT32 freq = 0;
    UINT8 lock = 200;
    UINT32 cur_f, last_f, cur_rs, last_rs, ch_num,temp_swap;
    INT32 i = 0;

/**************** Get Current Time for Debug Convenient****************/
    struct nim_s3501_private *dev_priv;
    date_time dt;
    get_local_time(&dt);
    AUTOSCAN_PRINTF("\nStep into nim_s3501_FFT:Blind Scan Current 90MHZ BW time %d:%d:%d\n",dt.hour,dt.min,dt.sec);

    dev_priv = dev->priv;
    freq = startFreq;

    //clear interrupt for software search
    if (dev_priv->ul_status.s3501_autoscan_stop_flag)
    {
        NIM_PRINTF("    leave fuction nim_s3501_FFT\n");
        return SUCCESS;
    }


    if (nim_s3501_i2c_open(dev))
        return S3501_ERR_I2C_NO_ACK;

    dev_priv->nim_Tuner_Control(dev_priv->tuner_id, freq, 0);

    comm_delay(0xffff*4);
    if (dev_priv->nim_Tuner_Status != NULL)
        dev_priv->nim_Tuner_Status(dev_priv->tuner_id, &lock);

    if (nim_s3501_i2c_close(dev))
        return S3501_ERR_I2C_NO_ACK;

    dev_priv->ul_status.m_CRNum = 0;
    dev_priv->ul_status.m_StepFreq = nim_s3501_fft_find_channel(dev, &startFreq);

    AUTOSCAN_PRINTF("\tCurrent Time Blind Scan Range: From  %dMHz to %dMHz  have find %d channels\n", startFreq - 45, startFreq + 45, dev_priv->ul_status.m_CRNum);
    for (i = 0; i < dev_priv->ul_status.m_CRNum; i++)
    {
        AUTOSCAN_PRINTF("\tTP -> %d. Freq = %d, Rs = %d\n", i, dev_priv->ul_status.m_Freq[i], dev_priv->ul_status.m_Rs[i]);
    }

    {
        if (dev_priv->ul_status.m_CRNum > 1)
        {
            ch_num = dev_priv->ul_status.m_CRNum;
            for (i = 1; i < dev_priv->ul_status.m_CRNum; i++)
            {
                cur_f = dev_priv->ul_status.m_Freq[i];
                last_f = dev_priv->ul_status.m_Freq[i-1];

                cur_rs = dev_priv->ul_status.m_Rs[i];
                last_rs = dev_priv->ul_status.m_Rs[i-1];

                if (cur_f-last_f < (cur_rs+last_rs)/2000)
                {
                    cur_f = last_f + (cur_f-last_f)*cur_rs/(cur_rs+last_rs);
                    cur_rs = last_rs + (cur_f-last_f)*2000;
                    dev_priv->ul_status.m_Freq[ch_num] = cur_f;
                    dev_priv->ul_status.m_Rs[ch_num] = cur_rs;
                    ch_num ++;
                    AUTOSCAN_PRINTF("\tError detected TP, modified to -> %d. Freq=%d, Rs=%d\n",ch_num,cur_f,cur_rs);
                }
            }
            if (dev_priv->ul_status.m_CRNum < ch_num){
                AUTOSCAN_PRINTF("current FFT result is:\n");
                for (i=0;i<1024;i=i+1)
                    AUTOSCAN_PRINTF("%d\n",FFT_energy_1024[i]);
            }
            dev_priv->ul_status.m_CRNum = ch_num;
        }
		
        for (i = 0; i < dev_priv->ul_status.m_CRNum; i++)
        {
			;        
		}
    }

    return SUCCESS;
}

static const UINT32 agc_table[256] = {
      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   1,   2,   3,   4,   5, \
  6,   7,   8,   9,  10,  11,  12,  13,  14,  15,  16,  17,  18,  19,  20,  21, \
 22,  23,  24,  25,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37, \
 38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  52,  55,  57, \
 60,  62,  65,  67,  70,  72,  75,  77,  78,  79,  80,  82,  83,  84,  85,  87, \
 88,  89,  90,  92,  93,  94,  95,  97,  98,  99, 101, 102, 103, 104, 105, 106, \
107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, \
123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, \
139, 140, 141, 142, 144, 145, 146, 147, 149, 150, 151, 152, 154, 155, 156, 157, \
159, 160, 161, 162, 164, 165, 166, 167, 169, 170, 171, 173, 175, 176, 178, 180, \
182, 184, 187, 189, 192, 194, 197, 199, 202, 204, 207, 209, 212, 215, 218, 222, \
225, 228, 232, 235, 238, 242, 245, 248, 252, 255, 258, 262, 265, 268, 272, 275, \
278, 282, 285, 288, 292, 295, 298, 302, 305, 308, 312, 317, 322, 327, 332, 337, \
342, 347, 352, 357, 362, 367, 372, 377, 382, 387, 392, 397, 402, 407, 412, 417, \
422, 427, 432, 437, 442, 447, 452, 457, 462, 467, 472, 477, 482, 487, 492, 493, \
494, 495, 496, 497, 498, 499, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, \
};
/****************************************************************************************************
   For each evaluation TP,soft search HW Time out cnt can never great than 2 times.
****************************************************************************************************/
INT32 nim_s3501_soft_search(struct nim_device *dev, UINT32 *est_rs, UINT32 *est_freq, INT32 delfreq)
{
    UINT8 data, ver_data[2];
    UINT8 intindex;
    UINT8 intdata;
    UINT8 lock_monitor = 0;
    UINT8 modcod;
    UINT32 tempFreq, freq;
    UINT32 Rs, rs, rs_rev=0;
    UINT8 code_rate, map_type, work_mode;
    UINT32 temp;
    UINT32 timeout;
    UINT32 tr_lock_thr;
    UINT32 cr_lock_thr;
    UINT32 fs_lock_thr;
    UINT32 ck_lock_num;
    UINT32 tr_lock_num;
    UINT32 cr_lock_num;
    UINT8 low_sym = 0;
    UINT8 lock = 200;
    UINT32 hwtmout_cnt = 0;
    UINT8 level, quality, dcc_i, dcc_q;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    UINT32 tr_lock_flag = 0;
    UINT32 cr_lock_flag = 0;
    UINT32 loss_lock_cnt = 0;
    UINT32 last_freq, last_rs;
    INT32 del_freq, del_rs;
    UINT32 s2_lock_cnt = 0;
    UINT32 RS_Search_Range=0, RS_Search_Step=0;

    rs = *est_rs;
    freq = *est_freq;
	
    NIM_PRINTF("Enter nim_s3501_soft_search:\n");
    AUTOSCAN_PRINTF("       Try TP : Freq = %4d, RS = %5d, delfc = %6d:\n ",freq,rs,delfreq);

    //reset
    nim_s3501_demod_ctrl(dev, NIM_DEMOD_CTRL_0X91);

    if ((0 == freq) || (0 == rs))
    {
        return ERR_FAILED;
    }

    low_sym = 0; 
    nim_s3501_freq_offset_set(dev, low_sym, &freq);

    if (nim_s3501_i2c_open(dev))
        return S3501_ERR_I2C_NO_ACK;

    comm_delay(500);

    Rs=rs;
    
    if(rs<2500)
        tr_lock_thr = 10;
    else
        tr_lock_thr = 6;
    
    if(rs<=3000)
        cr_lock_thr = 30;
    else if(rs<10000)
        cr_lock_thr = 20;
    else
        cr_lock_thr = 15;
    
    fs_lock_thr = cr_lock_thr+10;

    if(15000>=CRYSTAL_FREQ)
    {
       // if(Rs<44000)
        if(Rs<(CRYSTAL_FREQ*440/135))
            nim_s3501_set_dsp_clk (dev, 0);
        else
            nim_s3501_set_dsp_clk (dev, 3);
    }
    else
    {
        nim_s3501_set_dsp_clk (dev, 0);
    }

    if (priv->nim_Tuner_Control != NULL)
        priv->nim_Tuner_Control(priv->tuner_id, freq, Rs);

    if (priv->nim_Tuner_Status != NULL)
        priv->nim_Tuner_Status(priv->tuner_id, &lock);
    //NIM_PRINTF("Tuner lock = %d\n",lock);

    comm_delay(500);
    lock = 200;

    if (nim_s3501_i2c_close(dev))
        return S3501_ERR_I2C_NO_ACK;


    nim_s3501_interrupt_mask_clean(dev);

    data = 0xff;
    nim_s3501_set_hw_timeout(dev, data);

    nim_s3501_agc1_ctrl(dev, low_sym, NIM_OPTR_SOFT_SEARCH);

    nim_s3501_set_RS(dev, rs);

    nim_s3501_freq_offset_reset1(dev, low_sym, delfreq);

    nim_s3501_set_FC_Search_Range(dev, NIM_OPTR_SOFT_SEARCH, rs);

    nim_s3501_RS_Search_Range(dev, NIM_OPTR_SOFT_SEARCH, rs);

    if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
    {
        if((priv ->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C) && (priv->ul_status.m_tso_mode == 1))
        {
            nim_m3501c_close_dummy(dev); // include reset TSO
        }
    }

    #ifdef HW_ADPT_CR
    if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B && priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C)
    {
        data = 0xe1; 
        nim_reg_write(dev, R0E_ADPT_CR_CTRL, &data, 1);

        data = 0xf0 | 2;
        nim_reg_write(dev, R9D_RPT_DEMAP_BETA, &data, 1);
        if(rs<=10000) 
            data = 0x02;
        else
            data = 0x00;
        nim_reg_write(dev, R11_DCC_OF_I, &data, 1);
        data = 0x10;
        nim_reg_write(dev, R12_DCC_OF_Q, &data, 1);

        #ifdef HW_ADPT_CR_MONITOR
            data = 0xe6; 
        #else
            data = 0xe0; 
        #endif

        nim_reg_write(dev, R0E_ADPT_CR_CTRL, &data, 1);
    }
    #endif 

    nim_s3501_demod_ctrl(dev, NIM_DEMOD_CTRL_0X51);

    timeout = 0;
    lock_monitor = 0;
    hwtmout_cnt = 0;
    ck_lock_num = 0;
    tr_lock_num = 0;
    cr_lock_num = 0;
    last_freq = 0;
    last_rs = 0;
    while (1)
    {
        if (lock==0)
        {
            priv->nim_Tuner_Status(priv->tuner_id, &lock);
            if (lock==1)
            {
                NIM_PRINTF("Tuner lock = %d at timeout = %d\n",lock,timeout);
            }
        }
        timeout ++ ;
        if (100 < timeout)
        {
            nim_s3501_clear_int(dev);
            AUTOSCAN_PRINTF("\tTimeout \n");
            return ERR_FAILED;
        }
        if (priv->ul_status.s3501_autoscan_stop_flag)
        {
            AUTOSCAN_PRINTF("\tleave fuction nim_s3501_soft_search\n");
            return SUCCESS;
        }
        comm_sleep(50);
        
        //get the status
        
//#ifdef AUTOSCAN_DEBUG
//nim_s3501_reg_get_symbol_rate(dev, &Rs);
// tempFreq = freq;
//nim_s3501_reg_get_freq(dev, &tempFreq);
//NIM_PRINTF("        RS=%d , tempFreq= %d  \n", Rs,tempFreq);
//#endif

        intindex=0;
        nim_reg_read(dev, R04_STATUS, &intindex, 1);
        NIM_PRINTF("        Lock status is 0x%x\n", intindex);
        
        if (intindex&0x40)
            s2_lock_cnt ++;
        else
            s2_lock_cnt=0;
        
        if(intindex&0x18)
        {
            lock_monitor = lock_monitor | intindex;
            nim_s3501_reg_get_symbol_rate(dev, &Rs);
            tempFreq = freq;
            nim_s3501_reg_get_freq(dev, &tempFreq);
            nim_s3501_reg_get_work_mode(dev, &work_mode);
            
            del_freq = tempFreq - last_freq;
            if (del_freq < 0)
                del_freq = -del_freq;
            
            del_rs = Rs - last_rs;
            if (del_rs < 0)
                del_rs = -del_rs;
            
            if(del_freq<=2)
                cr_lock_num++;
            else
            {
                cr_lock_num = 0;
                last_freq = tempFreq;
                last_rs = Rs;
            }
            AUTOSCAN_PRINTF("       current lock Rs is %d,  Freq is %d at time %d\n",Rs, tempFreq,timeout);
            del_rs=rs_rev-Rs;
            if(del_rs<0)
                del_rs=-del_rs;
            if((Rs>=1000)&&(del_rs>=100))
                rs_rev=Rs;
            Rs=rs_rev;
            if ((work_mode==0) || (work_mode==1) || (s2_lock_cnt>4))
            {
                NIM_PRINTF("        lock chanel \n");
                NIM_PRINTF("            Freq is %d\n", (LNB_LOACL_FREQ - tempFreq));
                priv->ul_status.m_CurFreq = tempFreq ;
                NIM_PRINTF("            Rs is %d\n", Rs);
                nim_s3501_reg_get_code_rate(dev, &code_rate);
                NIM_PRINTF("            code_rate is %d\n", code_rate);
                NIM_PRINTF("            work_mode is %d\n", work_mode);
                nim_s3501_reg_get_map_type(dev, &map_type);
                NIM_PRINTF("            map_type is %d\n", map_type);
                AUTOSCAN_PRINTF("\tLock freq %d Rs %d with del_Rs %d del_f %d \n",(LNB_LOACL_FREQ - tempFreq),Rs,Rs-rs,tempFreq-freq);
                AUTOSCAN_PRINTF("work_mode= %d, s2_lock_cnt= %d \n",work_mode,s2_lock_cnt);
		  Final_Est_Freq=tempFreq;
		  Final_Est_SymRate=Rs;		
                #ifdef NIM_S3501_ASCAN_TOOLS
                nim_s3501_ascan_add_tp(ASCAN_ADD_REAL_TP,0x00,tempFreq, Rs, tempFreq>1550?1:0);
                #endif
                AUTOSCAN_PRINTF("\t timeout = %d\n",timeout);
                nim_s3501_clear_int(dev);
                return SUCCESS;
            }
        }

        if((intindex&0x04) == 0x04)
        {
            lock_monitor = lock_monitor | 0x04;
            tr_lock_num++;
            if (tr_lock_flag==0)
            {
                nim_s3501_reg_get_symbol_rate(dev, &Rs);
                NIM_PRINTF("        RS=%d \n", Rs);
                if(Rs<=1000)
                {
                    tr_lock_num--;
                }
                else
                {
                    tr_lock_flag = 1;
                    if(Rs<=3000)
                        cr_lock_thr = 30;
                    else if(Rs<10000)
                        cr_lock_thr = 20;
                    else
                        cr_lock_thr = 15;
                    fs_lock_thr = cr_lock_thr+10;
                    nim_s3501_demod_ctrl(dev, NIM_DEMOD_CTRL_0X91);
                    if (nim_s3501_i2c_open(dev))
                        return S3501_ERR_I2C_NO_ACK;
                    comm_delay(500);
                    if (priv->nim_Tuner_Control != NULL)
                        priv->nim_Tuner_Control(priv->tuner_id, freq, Rs);
                    if (priv->nim_Tuner_Status != NULL)
                        priv->nim_Tuner_Status(priv->tuner_id, &lock);
                    comm_delay(500);
                    if (nim_s3501_i2c_close(dev))
                        return S3501_ERR_I2C_NO_ACK;
                    lock = 200;
                    nim_s3501_agc1_ctrl(dev, low_sym, NIM_OPTR_CHL_CHANGE);
                    nim_s3501_set_RS(dev, Rs);
                    nim_s3501_set_FC_Search_Range(dev, NIM_OPTR_SOFT_SEARCH, Rs);
                    nim_s3501_clear_int(dev);
                    data = 0x00;
                    nim_reg_write(dev, R28_PL_TIMEOUT_BND,&data,1);
                    nim_reg_read(dev,R28_PL_TIMEOUT_BND+1,&data,1);
                    data &= 0xe0;
                    data |= 0x01;
                    nim_reg_write(dev,R28_PL_TIMEOUT_BND+1,&data,1);
                    if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
                    {
                        if((priv ->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C) && (priv->ul_status.m_tso_mode == 1))
                        {
                            nim_m3501c_close_dummy(dev); // include reset TSO
                        }
                    }
                    nim_s3501_demod_ctrl(dev, NIM_DEMOD_CTRL_0X51);
                    tr_lock_thr = tr_lock_thr-2;
                    lock_monitor = 0;
                    hwtmout_cnt = 0;
                    ck_lock_num = 0;
                    tr_lock_num = 0;
                    cr_lock_num = 0;
                    rs_rev=Rs;
                    timeout = 0;
                    AUTOSCAN_PRINTF("Reset: freq = %d, Rs = %d at time %d\n",freq,Rs,timeout);
                }
            }
        }


        if(tr_lock_thr == timeout)
        {
            if((lock_monitor&0xfc) == 0x00) 
            {
                AUTOSCAN_PRINTF("        Fault TP, exit without TR lock \n");
                return ERR_FAILED;
            }
        }
        else if(cr_lock_thr == timeout)
        {
            if(((lock_monitor&0xf8) == 0x00) &  (tr_lock_num<5))
            {
                AUTOSCAN_PRINTF("        Fault TP, exit without CR lock,  tr_lock_num %d\n",tr_lock_num);
                return ERR_FAILED;
            }
        }
        else if(fs_lock_thr == timeout)
        {
            if(((lock_monitor&0xf0) == 0x00) &  (cr_lock_num<5))
            {
                AUTOSCAN_PRINTF("        Fault TP, exit without Frame lock, cr_lock_num %d\n",cr_lock_num);
                return ERR_FAILED;
            }
        }

        nim_reg_read(dev, R00_CTRL, &intindex, 1);
        if (0 == (intindex & 0x10))
        {
            nim_reg_read(dev, R02_IERR, &intdata, 1);
            AUTOSCAN_PRINTF("        Interrupt register is 0x%02x\n", intdata);
            if (0x04 == (intdata & 0x04))
            {
                AUTOSCAN_PRINTF("        Can not lock chanel \n");
                return ERR_FAILED;
            }
            if (0x20 == (intdata & 0x20))
            {
                AUTOSCAN_PRINTF("        CR loss lock  \n");
            }
            if (0 != (intdata & 0x02))
            {
                NIM_PRINTF("        lock chanel \n");
                tempFreq = freq;
                nim_s3501_reg_get_freq(dev, &tempFreq);
                NIM_PRINTF("            Freq is %d\n", (LNB_LOACL_FREQ - tempFreq));
                priv->ul_status.m_CurFreq = tempFreq ;

                nim_s3501_reg_get_symbol_rate(dev, &Rs);
                NIM_PRINTF("            Rs is %d\n", Rs);
                del_rs=rs_rev-Rs;
                if(del_rs<0)
                    del_rs=-del_rs;
                if((Rs>=1000)&&(del_rs>=100))
                    rs_rev=Rs;
                Rs=rs_rev;

                nim_s3501_reg_get_code_rate(dev, &code_rate);
                NIM_PRINTF("            code_rate is %d\n", code_rate);

                nim_s3501_reg_get_work_mode(dev, &work_mode);
                NIM_PRINTF("            work_mode is %d\n", work_mode);

                nim_s3501_reg_get_map_type(dev, &map_type);
                NIM_PRINTF("            map_type is %d\n", map_type);

                if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
                {
                    if(priv ->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C)
                    {
                        nim_s3501_reg_get_modcod(dev, &modcod);
                        if(modcod & 0x01)
                            NIM_PRINTF("            Pilot on \n");
                        else
                        {
                            NIM_PRINTF("            Pilot off \n");
                        }
                        modcod = modcod>>1;
                        NIM_PRINTF("            Modcod is %x\n", modcod);
                    }
                }

		Final_Est_Freq=tempFreq;
		Final_Est_SymRate=Rs;
		//libc_printf("Pre_Add_TP_Para_path2:[%d,%d]\n",tempFreq,Rs);
				
                #ifdef NIM_S3501_ASCAN_TOOLS
                nim_s3501_ascan_add_tp(ASCAN_ADD_REAL_TP,0x00,tempFreq, Rs, tempFreq>1550?1:0);
                #endif

                AUTOSCAN_PRINTF("\tLock freq %d Rs %d with del_Rs %d del_f %d \n",(LNB_LOACL_FREQ - tempFreq),Rs,Rs-rs,tempFreq-freq);
                AUTOSCAN_PRINTF("\t timeout = %d\n",timeout);

                nim_s3501_clear_int(dev);
                return SUCCESS;
            }
            nim_s3501_clear_int(dev);
        }
    }
}

/*****************************************************************************
* INT32 nim_s3501_DiSEqC_operate(struct nim_device *dev, UINT32 mode, UINT8* cmd, UINT8 cnt)
*
*  defines DiSEqC operations
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT32 mode
*  Parameter3: UINT8* cmd
*  Parameter4: UINT8 cnt
*
* Return Value: void
*****************************************************************************/
static INT32 nim_s3501_DiSEqC_operate(struct nim_device *dev, UINT32 mode, UINT8 *cmd, UINT8 cnt)
{
    UINT8 data, temp;
    UINT16 timeout, timer;
    UINT8 i;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    NIM_PRINTF("mode = %d\n", mode);
    switch (mode)
    {
    case NIM_DISEQC_MODE_22KOFF:
    case NIM_DISEQC_MODE_22KON:
        nim_reg_read(dev, R7C_DISEQC_CTRL, &data, 1);
        data = ((data & 0xF8) | mode);
        nim_reg_write(dev, R7C_DISEQC_CTRL, &data, 1);
        break;
    case NIM_DISEQC_MODE_BURST0:
        nim_reg_read(dev, R7C_DISEQC_CTRL, &data, 1);
        //tone burst 0
        temp = 0x02;
        data = ((data & 0xF8) | temp);
        nim_reg_write(dev, R7C_DISEQC_CTRL, &data, 1);
        comm_sleep(16);
        break;
    case NIM_DISEQC_MODE_BURST1:
        nim_reg_read(dev, R7C_DISEQC_CTRL, &data, 1);
        //tone bust 1
        temp = 0x03;
        data = ((data & 0xF8) | temp);
        nim_reg_write(dev, R7C_DISEQC_CTRL, &data, 1);
        comm_sleep(16);
        break;
    case NIM_DISEQC_MODE_BYTES:
        comm_sleep(DISEQC_DELAY);
        nim_reg_read(dev, R7C_DISEQC_CTRL, &data, 1);
        va_22k=(data & 0x41);      // get DISEQC_22k origianl value
        if(cnt>8)
        {
            NIM_PRINTF("\t\t NIM_ERROR : Diseqc cnt larger than 8: cnt = %d\n", cnt);
            return ERR_FAILED;
        }
        else
        {
            // close 22K and set TX byte number
            data = ((data & 0xc0) | ((cnt - 1)<<3));
            nim_reg_write(dev, R7C_DISEQC_CTRL, &data, 1);
        }
        //write the tx data, max 8 byte
        for (i = 0; i < cnt; i++)
        {
            nim_reg_write(dev, (i + 0x7E), cmd + i, 1);
        }
	 // remove clean interrupt, since reg7d is read only
        //write the control bits, start TX
        temp = 0x04;
        data = ((data & 0xF8) | temp);
        nim_reg_write(dev, R7C_DISEQC_CTRL, &data, 1);

         //wait for DISEQC_EVENT register cleared before every transmission
        comm_sleep(1);  

        //waiting for the send over
        timer = 0;
        timeout = 75 + 13 * cnt;
        while (timer < timeout)
        {
            nim_reg_read(dev, R7C_DISEQC_CTRL + 0x01, &data, 1);
            if ((0 != (data & 0x07))&&(timer>50))
            {
                break;
            }
            comm_sleep(10);
            timer += 10;
        }
        if (1 == (data & 0x07))
        {
            //resume DISEQC_22k origianl value
            nim_reg_read(dev, R7C_DISEQC_CTRL, &data, 1);
            data = ((data & 0xB8) | (va_22k));
            nim_reg_write(dev, R7C_DISEQC_CTRL, &data, 1);
            return  SUCCESS;
        }
        else
        {
            //resume DISEQC_22k origianl value
            nim_reg_read(dev, R7C_DISEQC_CTRL, &data, 1);            
            data = ((data & 0xB8) | (va_22k));
            nim_reg_write(dev, R7C_DISEQC_CTRL, &data, 1);
            return ERR_FAILED;
        }
        break;
    case NIM_DISEQC_MODE_BYTES_EXT_STEP1:
        comm_sleep(DISEQC_DELAY);
        nim_reg_read(dev, R7C_DISEQC_CTRL, &data, 1);
        va_22k=(data & 0x41);      // get DISEQC_22k origianl value
        if(cnt>8)
        {
            NIM_PRINTF("\t\t NIM_ERROR : Diseqc cnt larger than 8: cnt = %d\n", cnt);
            return ERR_FAILED;
        }
        else
        {
            // close 22K and set TX byte number
            data = ((data & 0xc0) | ((cnt - 1)<<3));
            nim_reg_write(dev, R7C_DISEQC_CTRL, &data, 1);
        }

        //write the data
        for (i = 0; i < cnt; i++)
        {
            nim_reg_write(dev, (i + 0x7E), cmd + i, 1);
        }
	 // remove clean interrupt, since reg7d is read only
        break;
    case NIM_DISEQC_MODE_BYTES_EXT_STEP2:
        //TX start : Send byte
        nim_reg_read(dev, R7C_DISEQC_CTRL, &data, 1);
        temp = 0x04;
        data = ((data & 0xF8) | temp);
        nim_reg_write(dev, R7C_DISEQC_CTRL, &data, 1);

       //wait for DISEQC_EVENT register cleared before every transmission
        comm_sleep(1);  

        //waiting for the send over
        timer = 0;
        timeout = 75 + 13 * cnt;
        while (timer < timeout)
        {
            nim_reg_read(dev, R7C_DISEQC_CTRL + 0x01, &data, 1);
            if ((0 != (data & 0x07))&&(timer>50))
            {
                break;
            }
            comm_sleep(10);
            timer += 10;
        }
        if (1 == (data & 0x07))
        {
            //resume DISEQC_22k origianl value
            nim_reg_read(dev, R7C_DISEQC_CTRL, &data, 1);            
            data = ((data & 0xB8) | (va_22k));
            nim_reg_write(dev, R7C_DISEQC_CTRL, &data, 1);
            
            return SUCCESS;
        }
        else
        {
            //resume DISEQC_22k origianl value
            nim_reg_read(dev, R7C_DISEQC_CTRL, &data, 1);            
            data = ((data & 0xB8) | (va_22k));
            nim_reg_write(dev, R7C_DISEQC_CTRL, &data, 1);
            
            return ERR_FAILED;
        }
        break;
    case NIM_DISEQC_MODE_ENVELOP_ON:
        {
            nim_reg_read(dev, R24_MATCH_FILTER, &data, 1);
            data |= 0x01;
            nim_reg_write(dev, R24_MATCH_FILTER, &data, 1);
        }
        break;
    case NIM_DISEQC_MODE_ENVELOP_OFF:
        {
            nim_reg_read(dev, R24_MATCH_FILTER, &data, 1);
            data &= 0xFE;
            nim_reg_write(dev, R24_MATCH_FILTER, &data, 1);
        }
        break;
    default :
        break;       
    }
    return SUCCESS;
}

/*****************************************************************************
* INT32 nim_s3501_DiSEqC2X_operate(struct nim_device *dev, UINT32 mode, UINT8* cmd, UINT8 cnt, \
*                               UINT8 *rt_value, UINT8 *rt_cnt)
*
*  defines DiSEqC 2.X operations
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT32 mode
*  Parameter3: UINT8* cmd
*  Parameter4: UINT8 cnt
*  Parameter5: UINT8 *rt_value
*  Parameter6: UINT8 *rt_cnt
*
* Return Value: Operation result.
*****************************************************************************/
static INT32 nim_s3501_DiSEqC2X_operate(struct nim_device *dev, UINT32 mode, UINT8 *cmd, UINT8 cnt, UINT8 *rt_value, UINT8 *rt_cnt)
{
    INT32 result;
    UINT8 data, temp;
    UINT16 timeout, timer;
    UINT8 i;

    switch (mode)
    {
    case NIM_DISEQC_MODE_BYTES:
        //write the data to send buffer
        if(cnt>8)
        {
            NIM_PRINTF("\t\t NIM_ERROR : Diseqc cnt larger than 8: cnt = %d\n", cnt);
            return ERR_FAILED;
        }
        for (i = 0; i < cnt; i++)
        {
            data = cmd[i];
            nim_reg_write(dev, (i + R7C_DISEQC_CTRL + 0x02), &data, 1);
        }

        //set diseqc data counter
        temp = cnt - 1;
        nim_reg_read(dev, R7C_DISEQC_CTRL, &data, 1);
        va_22k=(data & 0x41);      // get DISEQC_22k origianl value
        data = ((data & 0x47) | (temp << 3));
        nim_reg_write(dev, R7C_DISEQC_CTRL, &data, 1);

        //enable diseqc interrupt mask event bit.
        nim_reg_read(dev, R03_IMASK, &data, 1);
        data |= 0x80;
        nim_reg_write(dev, R03_IMASK, &data, 1);

        //clear co-responding diseqc interrupt event bit.
        nim_reg_read(dev, R02_IERR, &data, 1);
        data &= 0x7f;
        nim_reg_write(dev, R02_IERR, &data, 1);

        //write the control bits, need reply
        nim_reg_read(dev, R7C_DISEQC_CTRL, &data, 1);
        temp = 0x84;
        data = ((data & 0x78) | temp);
        nim_reg_write(dev, R7C_DISEQC_CTRL, &data, 1);

         //wait for DISEQC_EVENT register cleared before every transmission
        comm_sleep(1);  

        //waiting for the send over
        timer = 0;
        timeout = 75 + 13 * cnt + 200; 
        data = 0;

        //check diseqc interrupt state.
        while (timer < timeout)
        {
            nim_reg_read(dev, R02_IERR, &data, 1);
            if (0x80 == (data & 0x80)) 
            {
                break;
            }
            comm_sleep(10);
            timer += 1;
        }

        //init value for error happens.
        result = ERR_FAILUE;
        rt_value[0] = DISEQC2X_ERR_NO_REPLY;
        *rt_cnt = 0;
        if (0x80 == (data & 0x80)) 
        {
            nim_reg_read(dev, R7C_DISEQC_CTRL + 0x01, &data, 1);

            switch (data & 0x07)
            {
            case 1:
                *rt_cnt = (UINT8) ((data >> 4) & 0x0f);
                if (*rt_cnt > 0)
                {
                    for (i = 0; i < *rt_cnt; i++)
                    {
                        nim_reg_read(dev, (i + R86_DISEQC_RDATA), (rt_value + i), 1);
                    }
                    result = SUCCESS;
                }

                break;

            case 2:
                rt_value[0] = DISEQC2X_ERR_NO_REPLY;
                break;
            case 3:
                rt_value[0] = DISEQC2X_ERR_REPLY_PARITY;
                break;
            case 4:
                rt_value[0] = DISEQC2X_ERR_REPLY_UNKNOWN;
                break;
            case 5:
                rt_value[0] = DISEQC2X_ERR_REPLY_BUF_FUL;
                break;
            default:
                rt_value[0] = DISEQC2X_ERR_NO_REPLY;
                break;
            }
        }

        //set 22k and polarity by origianl value; other-values are not care.
        nim_reg_read(dev, R7C_DISEQC_CTRL, &data, 1);
	    data = (data&0xb8) | va_22k;
        nim_reg_write(dev, R7C_DISEQC_CTRL, &data, 1);

        return result;

    default :
        break;
    }

    comm_sleep(1000);

    return SUCCESS;
}


/*****************************************************************************
* INT32 nim_s3501_get_lock(struct nim_device *dev, UINT8 *lock)
*
*  Read FEC lock status
*
*Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: BOOL *fec_lock
*
*Return Value: INT32
*****************************************************************************/
static INT32 nim_s3501_get_lock(struct nim_device *dev, UINT8 *lock)
{
    UINT8 data[3];
    UINT8 rdata;
    UINT8 h8psk_lock;
    INT32 freq_offset;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;

    comm_delay(150);
    nim_reg_read(dev, R02_IERR, data, 3);


    if ((priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B) && ( (priv->tsk_status.m_lock_flag != NIM_LOCK_STUS_NORMAL)))
    {
        if((priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C) && (priv->ul_status.m_tso_mode == 1))
        {
              if((data[0] & 0x02) != 0)
              {
                  nim_m3501c_fec_ts_off(dev);
                  nim_m3501c_reset_tso(dev);
                  nim_m3501c_fec_ts_on(dev);
                  nim_reg_read(dev, R02_IERR, &rdata, 1);
                  rdata = rdata & 0xfd;
                  nim_reg_write(dev, R02_IERR, &rdata, 1);
                  NIM_PRINTF("            Unlock and reset tso \n");
              }
        }
    }
	
    if ((data[2] & 0x80) == 0x80)
        h8psk_lock = 1;
    else
        h8psk_lock = 0;
    if ((h8psk_lock & ((data[2] & 0x2f) == 0x2f)) || ((h8psk_lock == 0) && ((data[2] & 0x3f) == 0x3f)))
    {
        *lock = 1;
        if ((priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B) && ( (priv->tsk_status.m_lock_flag != NIM_LOCK_STUS_NORMAL)))
        {
            if((priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C) && (priv->ul_status.m_tso_mode == 1))
            {	
                if((priv->ul_status.m_tso_status != NIM_TSO_STUS_LOCK))
    	         {
#ifdef C3501C_ERRJ_LOCK
                    nim_m3501c_recover_moerrj(dev);
#endif
    		      priv->ul_status.m_tso_status = NIM_TSO_STUS_LOCK;
        	  }
            }
        }
    }
    else
    {
        *lock = 0;
        if ((priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B) && ( (priv->tsk_status.m_lock_flag != NIM_LOCK_STUS_NORMAL)))
        {
            if((priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C) && (priv->ul_status.m_tso_mode == 1))
            {
            	    if(priv->ul_status.m_tso_status != NIM_TSO_STUS_SETTING)
         	    {
#ifdef C3501C_ERRJ_LOCK
                      nim_m3501c_invert_moerrj(dev);
#endif
    		        priv->ul_status.m_tso_status = NIM_TSO_STUS_SETTING;
          	    }
             }
        }
    }
    comm_delay(150);
    return SUCCESS;
}

/*****************************************************************************
* INT32 nim_s3501_get_freq(struct nim_device *dev, UINT32 *freq)
* Read S3501 frequence
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT16 *sym_rate         : Symbol rate in kHz
*
* Return Value: void
*****************************************************************************/
static INT32 nim_s3501_get_freq(struct nim_device *dev, UINT32 *freq)
{
    nim_s3501_reg_get_freq(dev, freq);
    return SUCCESS;
}

/*****************************************************************************
* INT32 nim_s3501_get_symbol_rate(struct nim_device *dev, UINT32 *sym_rate)
* Read S3501 symbol rate
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT16 *sym_rate         : Symbol rate in kHz
*
* Return Value: void
*****************************************************************************/
static INT32 nim_s3501_get_symbol_rate(struct nim_device *dev, UINT32 *sym_rate)
{
    nim_s3501_reg_get_symbol_rate(dev, sym_rate);
    return SUCCESS;
}

/*****************************************************************************
* INT32 nim_s3501_get_code_rate(struct nim_device *dev, UINT8* code_rate)
* Description: Read S3501 code rate
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT8* code_rate
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_s3501_get_code_rate(struct nim_device *dev, UINT8 *code_rate)
{
    nim_s3501_reg_get_code_rate(dev, code_rate);
    return SUCCESS;
}

/*****************************************************************************
* INT32 nim_s3501_get_AGC(struct nim_device *dev, UINT8 *agc)
*
*  This function will access the NIM to determine the AGC feedback value
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT16* agc
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_s3501_get_AGC(struct nim_device *dev, UINT8 *agc)
{
    UINT8 data;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;

    if (priv->Tuner_Config_Data.QPSK_Config & M3501_SIGNAL_DISPLAY_LIN)
    {
        nim_reg_read(dev, R04_STATUS, &data, 1);
        if (data & 0x01)
        {
            nim_reg_read(dev, R0A_AGC1_LCK_CMD + 0x01, &data, 1); 
            if (data > 0x7f)
                *agc = data - 0x80;
            else
                *agc = data + 0x80;
            return SUCCESS;
        }
        else
            *agc = 0;
    }
    else
    {
        nim_reg_read(dev, R07_AGC1_CTRL + 0x04, &data, 1);
        data = 255 - data;

        if (0x40 <= data)
            data -= 0x40;
        else if ((0x20 <= data) || (0x40 > data))
            data -= 0x20;
        else
            data -= 0;

        data /= 2;
        data += 16;
        *agc = (UINT8) data;
    }
    return SUCCESS;
}

/*****************************************************************************
* INT32 nim_s3501_get_BER(struct nim_device *dev, UINT32 *RsUbc)
* Get bit error ratio
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT16* RsUbc
*
* Return Value: INT32
*****************************************************************************/

#ifdef NIM_3501_FUNC_EXT

static INT32 nim_s3501_s2_get_BER(struct nim_device *dev, UINT32 *RsUbc)
{
    UINT8 data, rdata;
    UINT8 ber_data[3];
    UINT32 u32ber_data[3];
    UINT32 uber_data;

    nim_reg_read(dev, RD3_BER_REG, &rdata, 1);
    data = rdata & 0x7b;
    nim_reg_write(dev, RD3_BER_REG, &data, 1);

    nim_reg_read(dev, RD3_BER_REG + 0x01, &ber_data[0], 1);
    u32ber_data[0] = (UINT32) ber_data[0];
    nim_reg_read(dev, RD3_BER_REG + 0x01, &ber_data[1], 1);
    u32ber_data[1] = (UINT32) ber_data[1];
    u32ber_data[1] <<= 8;
    uber_data = u32ber_data[1] + u32ber_data[0];
    *RsUbc = uber_data;

    return SUCCESS;
}
#endif

#ifdef NIM_3501_FUNC_EXT
static INT32 nim_get_symbol(struct nim_device *dev)
{
    UINT8 data;
    UINT32 i;

    for (i = 0; i < 5000; i++)
    {
        data = 0xc1;
        nim_reg_write(dev, RC8_BIST_TOLERATOR, &data, 1);
        nim_reg_read(dev, RC9_CR_OUT_IO_RPT, &data, 1);
        NIM_PRINTF("%02x", data);
        nim_reg_read(dev, RC9_CR_OUT_IO_RPT + 0x01, &data, 1);
        NIM_PRINTF("%02x\n", data);
    }
    data = 0xc0;
    nim_reg_write(dev, RC8_BIST_TOLERATOR, &data, 1);
}
#endif

/*****************************************************************************
* INT32 nim_s3501_get_SNR(struct nim_device *dev, UINT8 *snr)
*
* This function returns an approximate estimation of the SNR from the NIM
*  The Eb No is calculated using the SNR from the NIM, using the formula:
*     Eb ~     13312- M_SNR_H
*     -- =    ----------------  dB.
*     NO           683
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT16* RsUbc
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_s3501_get_SNR(struct nim_device *dev, UINT8 *snr)
{
    UINT8 lock; 
    UINT8 data;
    UINT32 tdata, iter_num,sym_rate; 
    int i, total_iter, sum;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;

    if (priv->Tuner_Config_Data.QPSK_Config & M3501_SIGNAL_DISPLAY_LIN)
    {
#define    TDATA_SUM_LIN 6
        nim_reg_read(dev, R04_STATUS, &data, 1);
        if (data & 0x08)
        {
            // CR lock
            sum = 0;
            total_iter = 0;
            for (i = 0; i < TDATA_SUM_LIN; i++)
            {
                nim_reg_read(dev, R45_CR_LCK_DETECT + 0x01, &data, 1);
                tdata = data << 8;
                nim_reg_read(dev, R45_CR_LCK_DETECT, &data, 1);
                tdata |= data;

                if (tdata & 0x8000)
                    tdata = 0x10000 - tdata;

                nim_s3501_get_LDPC(dev, &iter_num);
                total_iter += iter_num;
                tdata >>= 5;
                sum += tdata;
            }
            sum /= TDATA_SUM_LIN;
            total_iter /= TDATA_SUM_LIN;
            sum *= 3;
            sum /= 5;
            if (sum > 255)
                sum = 255;

            if (priv->t_Param.t_last_snr == -1)
            {
                *snr = sum;
                priv->t_Param.t_last_snr = *snr;
            }
            else
            {
                if (total_iter == priv->t_Param.t_last_iter)
                {
                    *snr = priv->t_Param.t_last_snr;
                    if (sum > priv->t_Param.t_last_snr)
                        priv->t_Param.t_last_snr ++;
                    else if (sum < priv->t_Param.t_last_snr)
                        priv->t_Param.t_last_snr --;
                }
                else if ((total_iter > priv->t_Param.t_last_iter) && (sum < priv->t_Param.t_last_snr))
                {
                    *snr = sum;
                    priv->t_Param.t_last_snr = sum;
                }
                else if ((total_iter < priv->t_Param.t_last_iter) && (sum > priv->t_Param.t_last_snr))
                {
                    *snr = sum;
                    priv->t_Param.t_last_snr = sum;
                }
                else
                {
                    *snr = priv->t_Param.t_last_snr;
                }
            }
            priv->t_Param.t_last_iter = total_iter;
        }
        else
        {
            // CR unlock
            *snr = 0;
            priv->t_Param.t_last_snr = -1;
            priv->t_Param.t_last_iter = -1;
        }
    }
    else
    {
#define    TDATA_START 7
#define    TDATA_SUM 4

        tdata = 0;
        for (i = 0; i < TDATA_SUM; i++)
        {
            data = snr_tab[nim_s3501_get_SNR_index(dev)];
            tdata += data;
        }
        tdata /= TDATA_SUM;
        //CR04
        nim_reg_read(dev, R04_STATUS, &data, 1);
        if ((0x3F == data) | (0x7f == data) | (0xaf == data))
        {
        }
        else
        {
#if (SIGNAL_INDICATOR == 3)
            tdata = 0;
            if (0x01 == (data & 0x01)) //agc1
                tdata += 2;

            if (0x02 == (data & 0x02)) //agc2
                tdata += 10;

            if (0x04 == (data & 0x04)) //cr
                tdata += 16;

            if (0x08 == (data & 0x08)) //tr
                tdata += 16;

            if (0x10 == (data & 0x10)) //frame sync
                tdata += 20;

            if (0x20 == (data & 0x20))
                tdata += 26;
            if (0x40 == (data & 0x40))
                tdata += 26;

            if (0x80 == (data & 0x80))
                tdata += 26;
#else
            tdata = TDATA_START;
            if (0x01 == (data & 0x01))
                tdata += 1;

            if (0x02 == (data & 0x02))
                tdata += 3;

            if (0x04 == (data & 0x04))
                tdata += 3;

            if (0x08 == (data & 0x08))
                tdata += 2;

            if (0x10 == (data & 0x10))
                tdata += 2;

            if (0x20 == (data & 0x20))
                tdata += 2;

            if (0x40 == (data & 0x40))
                tdata += 2;

            if (0x80 == (data & 0x80))
                tdata += 2;
#endif
        }
        *snr = tdata / 2;
    }

    if (priv->t_Param.t_aver_snr == -1)
    {
        priv->t_Param.t_aver_snr = (*snr);
    }
    else
    {
        priv->t_Param.t_aver_snr += (((*snr) - priv->t_Param.t_aver_snr) >> 2);
    }

    nim_s3501_get_lock(dev, &lock);
    nim_s3501_get_symbol_rate(dev, &sym_rate);
    if((priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B) && (priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501B))
 {
    if((lock) && (sym_rate<=3000))
    {
            data = 0x42;
            nim_reg_write(dev, R33_CR_CTRL + 0x04, &data, 1);
            data = 0x8a;
            nim_reg_write(dev, RB5_CR_PRS_TRA, &data, 1);
    }
    else if ((lock) && (priv->t_Param.t_phase_noise_detected == 0) && (priv->t_Param.phase_noise_detect_finish == 1))
    {
        if (priv->t_Param.t_snr_state == 0)
        {
            priv->t_Param.t_snr_state = 1;
            data = 0x52;
            nim_reg_write(dev, R33_CR_CTRL + 0x04, &data, 1);
            data = 0xba;
            nim_reg_write(dev, RB5_CR_PRS_TRA, &data, 1);
            NIM_PRINTF("@@@@initial snr state = 1, reg37 = 0x52, regb5 = 0xba;\n");
        }
        if ((priv->t_Param.t_snr_state == 1) && (priv->t_Param.t_aver_snr > priv->t_Param.t_snr_thre1))
        {
            data = 0x4e;
            nim_reg_write(dev, R33_CR_CTRL + 0x04, &data, 1);
            data = 0xaa;
            nim_reg_write(dev, RB5_CR_PRS_TRA, &data, 1);
            NIM_PRINTF("snr state = 2, reg37 = 0x4e, regb5 = 0xaa;\n");
            priv->t_Param.t_snr_state = 2;
        }
        else if (priv->t_Param.t_snr_state == 2)
        {
            if (priv->t_Param.t_aver_snr > priv->t_Param.t_snr_thre2)
            {
                data = 0x42;
                nim_reg_write(dev, R33_CR_CTRL + 0x04, &data, 1);
                data = 0x9a;
                nim_reg_write(dev, RB5_CR_PRS_TRA, &data, 1);
                NIM_PRINTF("snr state = 3, reg37 = 0x42, regb5 = 0x9a;\n");
                priv->t_Param.t_snr_state = 3;
            }
            else if (priv->t_Param.t_aver_snr < (priv->t_Param.t_snr_thre1 - 5))
            {
                data = 0x52;
                nim_reg_write(dev, R33_CR_CTRL + 0x04, &data, 1);
                data = 0xba;
                nim_reg_write(dev, RB5_CR_PRS_TRA, &data, 1);
                NIM_PRINTF("snr state = 1, reg37 = 0x52, regb5 = 0xba;\n");
                priv->t_Param.t_snr_state = 1;
            }
        }
        else if (priv->t_Param.t_snr_state == 3)
        {
            if (priv->t_Param.t_aver_snr > priv->t_Param.t_snr_thre3)
            {
                data = 0x3e;
                nim_reg_write(dev, R33_CR_CTRL + 0x04, &data, 1);
                data = 0x8a;
                nim_reg_write(dev, RB5_CR_PRS_TRA, &data, 1);
                NIM_PRINTF("snr state = 4, reg37 = 0x3e, regb5 = 0x8a;\n");
                priv->t_Param.t_snr_state = 4;
            }
            else if (priv->t_Param.t_aver_snr < (priv->t_Param.t_snr_thre2 - 5))
            {
                data = 0x4e;
                nim_reg_write(dev, R33_CR_CTRL + 0x04, &data, 1);
                data = 0xaa;
                nim_reg_write(dev, RB5_CR_PRS_TRA, &data, 1);
                NIM_PRINTF("snr state = 2, reg37 = 0x4e, regb5 = 0xaa;\n");
                priv->t_Param.t_snr_state = 2;
            }
        }
        else if (priv->t_Param.t_snr_state == 4)
        {
            if (priv->t_Param.t_aver_snr < (priv->t_Param.t_snr_thre3 - 5))
            {
                data = 0x42;
                nim_reg_write(dev, R33_CR_CTRL + 0x04, &data, 1);
                data = 0x9a;
                nim_reg_write(dev, RB5_CR_PRS_TRA, &data, 1);
                NIM_PRINTF("snr state = 3, reg37 = 0x42, regb5 = 0x9a;\n");
                priv->t_Param.t_snr_state = 3;
            }
        }
    }
}

    if (priv->t_Param.t_dynamic_power_en)
        nim_s3501_dynamic_power(dev, (*snr));

    return SUCCESS;
}


/*****************************************************************************
* INT32 nim_s3501_get_BER(struct nim_device *dev, UINT32 *RsUbc)
* Get bit error ratio
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT16* RsUbc
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_s3501_get_BER(struct nim_device *dev, UINT32 *RsUbc)
{
    UINT8 data;
    UINT8 ber_data[3];
    UINT32 u32ber_data[3];
    UINT32 uber_data;

    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    //CR78
    nim_reg_read(dev, R76_BIT_ERR + 0x02, &data, 1);
    if (0x00 == (0x80 & data))
    {
        //CR76
        nim_reg_read(dev, R76_BIT_ERR, &ber_data[0], 1);
        u32ber_data[0] = (UINT32) ber_data[0];
        //CR77
        nim_reg_read(dev, R76_BIT_ERR + 0x01, &ber_data[1], 1);
        u32ber_data[1] = (UINT32) ber_data[1];
        u32ber_data[1] <<= 8;
        //CR78
        nim_reg_read(dev, R76_BIT_ERR + 0x02, &ber_data[2], 1);
        u32ber_data[2] = (UINT32) ber_data[2];
        u32ber_data[2] <<= 16;

        uber_data = u32ber_data[2] + u32ber_data[1] + u32ber_data[0];

        uber_data *= 100;
        uber_data /= 1632;
        *RsUbc = uber_data;
        priv->ul_status.old_ber = uber_data;
        //CR78
        data = 0x80;
        nim_reg_write(dev, R76_BIT_ERR + 0x02, &data, 1);
    }
    else
    {
        *RsUbc = priv->ul_status.old_ber;
    }

    return SUCCESS;
}

/*****************************************************************************
* INT32 nim_s3501_dynamic_power(struct nim_device *dev, UINT32 *RsUbc)
* Get bit error ratio
*
* Arguments:
* Parameter1:
* Key word: power_ctrl
* Return Value: INT32
*****************************************************************************/
static INT32 nim_s3501_dynamic_power(struct nim_device *dev, UINT8 snr)
{
    UINT8 coderate;
    UINT32 ber;
    static UINT32 ber_sum = 0; 
    static UINT32 last_ber_sum = 0;
    static UINT32 cur_ber_sum = 0;
    static UINT32 ber_thres = 0x180;
    static UINT8 cur_max_iter = 50; 
    static UINT8 snr_bak = 0;
    static UINT8 last_max_iter = 50;
    static int cnt3 = 0;

    if (cnt3 >= 3)
    {
        last_ber_sum = cur_ber_sum;
        cur_ber_sum = ber_sum;
        cnt3 = 0;
        ber_sum = 0;
    }
    nim_s3501_get_BER(dev, &ber);
    nim_s3501_reg_get_code_rate(dev, &coderate);
    ber_sum += ber;
    cnt3 ++;
    if (coderate < 0x04)   
        ber_thres = 0x120;
    else
        ber_thres = 0x70;


    if (cur_max_iter == 50)
    {
        if (ber_sum >= ber_thres)
        {
            if (snr > snr_bak)
                snr_bak = snr;
            cur_max_iter -= 15;
        }
    }
    else if (cur_max_iter < 50)
    {
        if (((cur_ber_sum + 0x80) < last_ber_sum) || (snr > (snr_bak + 2)))
        {
            cur_max_iter += 15;
            snr_bak = 0;
            cnt3 = 0;
            ber_sum = 0;
            last_ber_sum = 0;
            cur_ber_sum = 0;
        }
        else if (ber_sum > 3 * ber_thres)
        {
            cur_max_iter -= 15;
            if ((coderate < 0x04) && (cur_max_iter < 35))
                cur_max_iter = 35;
            else if (cur_max_iter < 20)
                cur_max_iter = 20;
        }
    }

    if (cur_max_iter != last_max_iter)
    {
        NIM_PRINTF("----change cur_max_iter to %d----\n\n", cur_max_iter);
        nim_reg_write(dev, R57_LDPC_CTRL, &cur_max_iter, 1);
        last_max_iter = cur_max_iter;
    }
    return SUCCESS;
}


/*****************************************************************************
* INT32 nim_s3501_get_PER(struct nim_device *dev, UINT32 *RsUbc)
* Get packet error ratio
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT16* RsUbc
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_s3501_get_PER(struct nim_device *dev, UINT32 *RsUbc)
{
    UINT8 per[2];
    UINT16 percount;
    UINT8 data,verdata;
    static UINT8 flag0=1,flag1=0;
    static UINT32 cur_sym=0,last_sym=0;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;

    *RsUbc=1010;
    if((priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)&&(priv ->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C))
    {
        nim_reg_read(dev, R04_STATUS, &data, 1);
        if (0x00 != (0x20 & data))
        {
            nim_reg_read(dev, R79_PKT_ERR + 0x01, &per[1], 1);
            per[1] = per[1] & 0x7f;
            nim_reg_read(dev, R79_PKT_ERR, &per[0], 1);
            percount = (UINT16) (per[1] * 256 + per[0]);
            *RsUbc = (UINT32) percount;
            NIM_PRINTF("current PER is  %d\n", percount);

            nim_reg_read(dev, R70_CAP_REG + 0x01, &data, 1);
            data=0x7f&data;
            nim_reg_write(dev, R70_CAP_REG + 0x01, &data, 1);
            comm_delay(10);
            nim_reg_read(dev, R70_CAP_REG + 0x01, &data, 1);
            data=0x80|data;
            nim_reg_write(dev, R70_CAP_REG + 0x01, &data, 1);

            return SUCCESS;
        }
        else
        {
            return ERR_PARA;
        }
    }
    else
    {
        if(flag1==0)
        {
            data=0xff;
            nim_reg_write(dev, R74_PKT_STA_NUM, &data, 1);
            verdata=0xff;
            nim_reg_write(dev, R74_PKT_STA_NUM + 0x01, &verdata, 1);
            flag1=1;
        }
        comm_delay(10);
        nim_reg_read(dev, R04_STATUS, &data, 1);
        if (0x00 != (0x20 & data))
        {
            flag0=1;
            if( (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)&&(priv ->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501B))
            {
                nim_reg_read(dev, R9D_RPT_DEMAP_BETA+1, &verdata, 1);
                if(0x00==(0x80&verdata))
                {
                    flag0=0;
                    return ERR_PARA;
                }
            }

            if(flag0==1)
            {
                nim_reg_read(dev, R79_PKT_ERR + 0x01, &data, 1);
                if (0x00 == (0x80 & data))
                {
                    per[1]=data;
                    nim_reg_read(dev, R79_PKT_ERR, &per[0], 1);
                    per[1] = per[1] & 0x7f;
                    percount = (UINT16) (per[1] * 256 + per[0]);
                    *RsUbc = (UINT32) percount;
                    NIM_PRINTF("current PER is  %d\n", percount);
                    //CR7a
                    data = 0x80;
                    nim_reg_write(dev, R79_PKT_ERR + 0x01, &data, 1);
                }
                else
                {
                    nim_reg_read(dev, R79_PKT_ERR + 0x01, &data, 1);
                    data = (0xEF & data);
                    nim_reg_write(dev, R79_PKT_ERR + 0x01, &data, 1);
                    comm_delay(10);
                    per[1]=data;
                    nim_reg_read(dev, R79_PKT_ERR, &per[0], 1);
                    per[1] = per[1] & 0x7f;
                    percount = (UINT16) (per[1] * 256 + per[0]);
                    *RsUbc = (UINT32) percount;
                    NIM_PRINTF("current PER is  %d\n", percount);
                    //CR7a
                    data = 0x80;
                    nim_reg_write(dev, R79_PKT_ERR + 0x01, &data, 1);
                }
                return SUCCESS;
            }
        }
        else
        {
            return ERR_PARA;
        }
    }
}

/*****************************************************************************
* INT32 nim_s3501_get_LDPC(struct nim_device *dev, UINT32 *RsUbc)
* Get LDPC average iteration number
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT16* RsUbc
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_s3501_get_LDPC(struct nim_device *dev, UINT32 *RsUbc)
{
    UINT8 data;

    nim_reg_read(dev, RAA_S2_FEC_ITER, &data, 1);
    *RsUbc = (UINT32) data;
    return SUCCESS;
}

/*****************************************************************************
* INT32 nim_s3501_channel_search(struct nim_device *dev, UINT32 CRNum );
* Description: S3501 channel blind searching operation
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT32 freq              : Frequence
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_s3501_channel_search(struct nim_device *dev, UINT32 CRNum)
{
    UINT32 freq,freq0,freq1;
    INT32 k = 0;
    INT32 delfreq = 0,delfreq0=0;
    UINT32 sym;
    struct nim_s3501_private *dev_priv;
    dev_priv = (struct nim_s3501_private *)dev->priv;

    NIM_PRINTF("Enter nim_s3501_channel_search:\n");

    freq = dev_priv->ul_status.m_Freq[CRNum];
    sym = dev_priv->ul_status.m_Rs[CRNum];
    
    delfreq = freq % 1024;
    freq = freq>>10;
    if(delfreq>512)
    {
        delfreq=delfreq-1024;
        freq=freq+1;
    }
    dev_priv->ul_status.m_CurFreq = freq;
    
    AUTOSCAN_PRINTF("Enter nim_s3501_channel_search:TP -> %d. Freq=%d, Rs=%d\n", CRNum, freq, sym);
    
    if (SUCCESS == nim_s3501_soft_search(dev, &dev_priv->ul_status.m_Rs[CRNum], &freq, delfreq))
        return SUCCESS;
    else if((freq<=1846)&&(freq>=1845)&&(sym<=3000)&&(sym>=1500))/////Yatai5:12446/V/1537
    {
        AUTOSCAN_PRINTF("*************Try_again*************\n");
        if(delfreq<0)
        {
            freq0=freq;
            delfreq0=0;
        }
        else if(delfreq<400)
        {
            freq0=freq;
            delfreq0=0;
        }
        else
        {
            freq0=freq+1;
            delfreq0=0;
        }
        if (SUCCESS == nim_s3501_soft_search(dev, &dev_priv->ul_status.m_Rs[CRNum], &freq, delfreq))
            return SUCCESS;
        else if (SUCCESS == nim_s3501_soft_search(dev, &dev_priv->ul_status.m_Rs[CRNum], &freq0, delfreq0))
            return SUCCESS;
    }

#if 0
    if(NIM_SCAN_SLOW == dev_priv->blscan_mode)
    {
        if ((sym<5000) && (sym>3000))
        {
            for (k=0;k<2;k++)
            {
                switch(k)
                {
                case 0:
                    freq = dev_priv->ul_status.m_Freq[CRNum]-1024;
                    delfreq = freq % 1024;
                    freq = freq>>10;
                    break;
                case 1:
                    freq = dev_priv->ul_status.m_Freq[CRNum]+1024;
                    delfreq = freq % 1024;
                    freq = freq>>10;
                    break;
                }
                if (SUCCESS == nim_s3501_soft_search(dev, &dev_priv->ul_status.m_Rs[CRNum], &freq, delfreq))
                    return SUCCESS;
            }
        }
    }
#endif

    return ERR_FAILUE;
}

INT32 nim_s3501_reg_get_freq(struct nim_device *dev, UINT32 *freq)
{
    INT32 freq_off;
    UINT8 data[3];
    UINT32 sample_rate;
    UINT32 tdata;
    UINT32 temp;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    temp = 0;
    nim_reg_read(dev, R69_RPT_CARRIER, data, 3);
    tdata = (data[0]&0xff)  + ((data[1]&0xff) <<8);
    if ((data[2] & 0x01) == 1)
        temp = (tdata ^ 0xffff) + 1;
    else
        temp = tdata & 0xffff;

    nim_s3501_get_dsp_clk(dev, &sample_rate);

    if ((data[2] & 0x01) == 1)
        freq_off = 0 - nim_s3501_multu64div(temp, sample_rate, 92160000);
    else
        freq_off = nim_s3501_multu64div(temp, sample_rate, 92160000);
    *freq += freq_off;

    return SUCCESS;
}


INT32 nim_s3501_reg_get_code_rate(struct nim_device *dev, UINT8 *code_rate)
{
    UINT8 data;
    nim_reg_read(dev, R69_RPT_CARRIER + 0x02, &data, 1);
    *code_rate = ((data >> 1) & 0x0f);
    return SUCCESS;

    //  Code rate list
    //  for DVBS:
    //      0x0:    1/2,
    //      0x1:    2/3,
    //      0x2:    3/4,
    //      0x3:    5/6,
    //      0x4:    6/7,
    //      0x5:    7/8.
    //  For DVBS2 :
    //      0x0:    1/4 ,
    //      0x1:    1/3 ,
    //      0x2:    2/5 ,
    //      0x3:    1/2 ,
    //      0x4:    3/5 ,
    //      0x5:    2/3 ,
    //      0x6:    3/4 ,
    //      0x7:    4/5 ,
    //      0x8:    5/6 ,
    //      0x9:    8/9 ,
    //      0xa:    9/10.
}


static INT32 nim_s3501_reg_get_map_type(struct nim_device *dev, UINT8 *map_type)
{
    UINT8 data;
    nim_reg_read(dev, R69_RPT_CARRIER + 0x02, &data, 1);
    *map_type = ((data >> 5) & 0x07);
    return SUCCESS;

    //  Map type:
    //      0x0:    HBCD.
    //      0x1:    BPSK
    //      0x2:    QPSK
    //      0x3:    8PSK
    //      0x4:    16APSK
    //      0x5:    32APSK
}

static INT32 nim_s3501_reg_get_work_mode(struct nim_device *dev, UINT8 *work_mode)
{
    UINT8 data;
    nim_reg_read(dev, R68_WORK_MODE, &data, 1);
    *work_mode = data & 0x03;
    return SUCCESS;

    //  Work Mode
    //      0x0:    DVB-S
    //      0x1:    DVB-S2
    //      0x2:    DVB-S2 HBC
}

static INT32 nim_s3501_reg_get_iqswap_flag(struct nim_device *dev, UINT8 *iqswap_flag)
{
    UINT8 data;
    nim_reg_read(dev, R6C_RPT_SYM_RATE + 0x02, &data, 1);
    *iqswap_flag = ((data >> 4) & 0x01);
    return SUCCESS;
}

static INT32 nim_s3501_reg_get_roll_off(struct nim_device *dev, UINT8 *roll_off)
{
    UINT8 data;
    nim_reg_read(dev, R6C_RPT_SYM_RATE + 0x02, &data, 1);
    *roll_off = ((data >> 5) & 0x03);
    return SUCCESS;

    //  DVBS2 Roll off report
    //      0x0:    0.35
    //      0x1:    0.25
    //      0x2:    0.20
    //      0x3:    Reserved
}

static INT32 nim_s3501_get_dsp_clk(struct nim_device *dev, UINT32 *sample_rate)
{
    UINT8 data;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
    {
        if(priv ->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C)
        {
            nim_reg_read(dev, RF0_HW_TSO_CTRL, &data, 1);
            data = data & 0xc0;
            if(data == 0x00)
                *sample_rate = ((CRYSTAL_FREQ * 90 * 10 + 67) / 135);  ////uint is KHz
            else if(data == 0x40)
                *sample_rate = ((CRYSTAL_FREQ * 98 * 10  + 67) / 135);
            else if(data == 0x80)
                *sample_rate = ((CRYSTAL_FREQ * 108 * 10 + 67) / 135);
            else if(data == 0xc0)
                *sample_rate = ((CRYSTAL_FREQ * 135 *10 + 67) / 135);
        }
        else
            *sample_rate = ((CRYSTAL_FREQ * 90 * 10 + 67) / 135);
    }
    else
        *sample_rate = ((CRYSTAL_FREQ * 99 *10 + 67) / 135);
    return SUCCESS;
}

static INT32 nim_s3501_set_dsp_clk (struct nim_device *dev, UINT8 clk_sel)
{
    UINT8 data;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
    {
        if(priv ->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C)
        {
            nim_reg_read(dev, RF0_HW_TSO_CTRL, &data, 1);
            if((clk_sel<<6) != (data & 0xc0))
            {
                switch (clk_sel)
                {
                    case 0:
                        data = (data & 0x3f) | (0<<6) ;
                        nim_reg_write(dev, RF0_HW_TSO_CTRL, &data, 1);
                        data = ((CRYSTAL_FREQ * 90  + 6750) / 13500);
                        nim_reg_write(dev, R90_DISEQC_CLK_RATIO, &data, 1);
                        break;
                    case 1:
                        data = (data & 0x3f) | (1<<6) ;
                        nim_reg_write(dev, RF0_HW_TSO_CTRL, &data, 1);
                        data = ((CRYSTAL_FREQ * 98  + 6750) / 13500);
                        nim_reg_write(dev, R90_DISEQC_CLK_RATIO, &data, 1);
                        break;
                    case 2:
                        data = (data & 0x3f) | (2<<6) ;
                        nim_reg_write(dev, RF0_HW_TSO_CTRL, &data, 1);
                        data = ((CRYSTAL_FREQ * 108  + 6750) / 13500);
                        nim_reg_write(dev, R90_DISEQC_CLK_RATIO, &data, 1);
                        break;
                    case 3:
                        data = (data & 0x3f) | (3<<6) ;
                        nim_reg_write(dev, RF0_HW_TSO_CTRL, &data, 1);
                        data = ((CRYSTAL_FREQ * 135  + 6750) / 13500);
                        nim_reg_write(dev, R90_DISEQC_CLK_RATIO, &data, 1);
                        break;
                }
            }
        }
        else
        {
            NIM_PRINTF("Ali_NIM_Warning : M3501B need not set DSP clock\n");
        }
    }
    return SUCCESS;
}


static INT32 nim_m3501c_close_dummy (struct nim_device *dev)
{
    UINT8 data;
    nim_m3501c_fec_ts_off(dev);

    nim_reg_read(dev, RF0_HW_TSO_CTRL, &data, 1);
    data = data & 0xfd;
    nim_reg_write(dev, RF0_HW_TSO_CTRL, &data, 1);
    NIM_PRINTF("            nim m3501c close dummy for dvbs \n");

    nim_m3501c_reset_tso(dev);
    nim_m3501c_fec_ts_on(dev);
    return SUCCESS;
}

static INT32 nim_m3501c_open_dummy (struct nim_device *dev)
{
    UINT8 data;
    nim_m3501c_fec_ts_off(dev);

    nim_reg_read(dev, RF0_HW_TSO_CTRL, &data, 1);
    data = data | 0x02;
    nim_reg_write(dev, RF0_HW_TSO_CTRL, &data, 1);
    NIM_PRINTF("            nim m3501c insert dummy\n");

    nim_m3501c_reset_tso(dev);
    nim_m3501c_fec_ts_on(dev);
    return SUCCESS;
}



static INT32 nim_m3501c_invert_moerrj (struct nim_device *dev)
{
    UINT8 data;
    nim_reg_read(dev, RF0_HW_TSO_CTRL+6, &data, 1);
    data = data | 0x08;
    nim_reg_write(dev, RF0_HW_TSO_CTRL+6, &data, 1);
    NIM_PRINTF("            nim m3501c invert_moerrj \n");
    return SUCCESS;
}

static INT32 nim_m3501c_recover_moerrj (struct nim_device *dev)
{
    UINT8 data;
    nim_reg_read(dev, RF0_HW_TSO_CTRL+6, &data, 1);
    data = data & 0xf7;
    nim_reg_write(dev, RF0_HW_TSO_CTRL+6, &data, 1);
    NIM_PRINTF("            nim m3501c recover_moerrj\n");
    return SUCCESS;
}

static INT32 nim_m3501c_dvbs_ts_bps (struct nim_device *dev)
{
    UINT8 data;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;

    if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
    {
        if(priv ->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C)
        {
            NIM_PRINTF("            nim_m3501c_dvbs_ts_bps \n");
        }
    }
    return SUCCESS;
}

static INT32 nim_m3501c_enable_soft_cbr (struct nim_device *dev)
{
    return SUCCESS;
}

static INT32 nim_m3501c_disable_soft_cbr (struct nim_device *dev)
{
    return SUCCESS;
}

static INT32 nim_m3501_ts_on (struct nim_device *dev)
{
    UINT8 data;
    // close ts output
    nim_reg_read(dev, R9D_RPT_DEMAP_BETA+1, &data, 1);
    data = data | 0x80;
    nim_reg_write(dev, R9D_RPT_DEMAP_BETA+1, &data, 1);
    NIM_PRINTF("            Final ts off\n");
    return SUCCESS;
}

static INT32 nim_m3501_ts_off (struct nim_device *dev)
{
    UINT8 data;
    // Open ts output
    nim_reg_read(dev, R9D_RPT_DEMAP_BETA+1, &data, 1);
    data = data & 0x7f;
    nim_reg_write(dev, R9D_RPT_DEMAP_BETA+1, &data, 1);
    NIM_PRINTF("            Final ts on\n");
    return SUCCESS;
}

static INT32 nim_m3501c_fec_ts_off (struct nim_device *dev)
{
    UINT8 data;
    // close fec ts output
    nim_reg_read(dev, RAF_TSOUT_PAD, &data, 1);
    data = data | 0x10;
    nim_reg_write(dev, RAF_TSOUT_PAD, &data, 1);
    NIM_PRINTF("            fec ts off\n");
    return SUCCESS;
}

static INT32 nim_m3501c_fec_ts_on (struct nim_device *dev)
{
    UINT8 data;
    // Open fec ts output
    nim_reg_read(dev, RAF_TSOUT_PAD, &data, 1);
    data = data & 0xef;
    nim_reg_write(dev, RAF_TSOUT_PAD, &data, 1);
    NIM_PRINTF("            fec ts on\n");
    return SUCCESS;
}


static INT32 nim_m3501c_reset_tso (struct nim_device *dev)
{
    UINT8 data;

    // Enable soft reset
    data = 0xfe;
    nim_reg_write(dev, RFA_RESET_CTRL, &data, 1);

    // Enable tso soft reset
    data = 0xbf;
    nim_reg_write(dev, RFA_RESET_CTRL+4, &data, 1);
    data = 0xff;
    nim_reg_write(dev, RFA_RESET_CTRL+4, &data, 1);

    // Disable soft reset
    data = 0xff;
    nim_reg_write(dev, RFA_RESET_CTRL, &data, 1);

    NIM_PRINTF("            Reset TSO\n");
    return SUCCESS;
}

static INT32 nim_m3501c_get_int(struct nim_device *dev)
{
    UINT8 data, rdata;

    // Enable soft reset
    nim_reg_read(dev, R02_IERR, &rdata, 1);
    if((rdata & 0x02) != 0)
    {
        nim_m3501c_fec_ts_off(dev);
        nim_m3501c_reset_tso(dev);
        nim_m3501c_fec_ts_on(dev);
        data = rdata & 0xfd;
        nim_reg_write(dev, R02_IERR, &data, 1);
#ifdef C3501C_ERRJ_LOCK
        nim_m3501c_recover_moerrj(dev);
#endif
        NIM_PRINTF("            Found plug_in_out and reset tso \n");
    }
    return SUCCESS;
}

static INT32 nim_s3501_reg_get_modcod(struct nim_device *dev, UINT8 *modcod)
{
    UINT8 data;
    nim_reg_read(dev, RF8_MODCOD_RPT, &data, 1);
    *modcod = data & 0x7f;
    return SUCCESS;
}

static INT32 nim_s3501_get_tune_freq(struct nim_device *dev, INT32 *freq)
{
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;

    *freq += priv->ul_status.m_StepFreq;
    if (*freq <= 0)
        *freq = 30;
    else if (*freq > 70)
        *freq = 70;

    return SUCCESS;
}

INT32 nim_s3501_reg_get_symbol_rate(struct nim_device *dev, UINT32 *sym_rate)
{
    UINT8 data[3];
    UINT32 sample_rate;
    UINT32 symrate;
    unsigned   long long temp;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;

    nim_reg_read(dev, R6C_RPT_SYM_RATE, data, 3);
    symrate = (data[0]>>1) + (data[1] <<7)+((data[2]&0x01) <<15); // K s/s
    nim_s3501_get_dsp_clk(dev, &sample_rate);

//   *sym_rate = (symrate * sample_rate +46080) / 92160;
    *sym_rate = nim_s3501_multu64div(symrate, sample_rate, 92160);
    return SUCCESS;
}

void nim_s3501_set_CodeRate(struct nim_device *dev, UINT8 coderate)
{
    return;
}

void nim_s3501_set_RS(struct nim_device *dev, UINT32 rs)
{
    UINT8 data, ver_data[3];
    UINT32 sample_rate;
    UINT32 temp;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;

    if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
    {
        if((priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C) && (priv->ul_status.m_tso_mode == 1))
        {
            if(rs<7000) 
                data = 0x10;
            else if(rs<15000)
                data = 0x20;
            else if(rs<23000)
                data = 0x30;
            else if(rs<31000)
                data = 0x40;
            else if(rs<39000)
                data = 0x50;
            else if(rs<47000)
                data = 0x60;
            else if(rs<54000)
                data = 0x70;
            else if(rs<59000)
                data = 0x90;
            else if(rs<62000)
                data = 0xa0;
            else if(rs<65000)
                data = 0xb0;
            else
                data = 0xc0;
            nim_reg_write(dev, RF1_DSP_CLK_CTRL, &data, 1);
        }
    }

    temp = 0;
    nim_s3501_get_dsp_clk(dev, &sample_rate);

    NIM_PRINTF("\t\t set ts %d with dsp clock %d\n", rs, sample_rate/1000);
    temp = nim_s3501_multu64div(rs, 184320, sample_rate); // 184320 == 90000 * 2.048

    if(temp>0x16800)
    {    
        NIM_PRINTF("\t\t NIM_Error: Symbol rate set error %d\n", temp);
        temp = 0x16800;
    }
    //CR3F
    ver_data[0] = (UINT8) (temp & 0xFF);
    ver_data[1] = (UINT8) ((temp & 0xFF00) >> 8);
    ver_data[2] = (UINT8) ((temp & 0x10000) >> 16);
    nim_reg_write(dev, R5F_ACQ_SYM_RATE, ver_data, 3);
}


void nim_s3501_set_freq_offset(struct nim_device *dev, INT32 delfreq)
{
    UINT8 data[3];
    UINT32 temp, delfreq_abs;
    UINT32 sample_rate;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;

    //delfreq unit is KHz, sample_rate unit is KHz
    if(delfreq<0)
        delfreq_abs = 0 - delfreq;
    else
	 delfreq_abs = delfreq;
    nim_s3501_get_dsp_clk(dev, &sample_rate);
    temp = nim_s3501_multu64div(delfreq_abs, 92160, sample_rate); // 92160 == 90000 * 1.024
    if(temp>0xffff)
    {    
        NIM_PRINTF("\t\t NIM_Error: Symbol rate set error %d\n", temp);
        temp = 0xffff;
    }

    if(delfreq<0)
        temp = temp^0xffff | 0x10000;
    else
        temp = temp&0xffff ;
    //CR5C
    data[0] = (UINT8) (temp & 0xFF);
    data[1] = (UINT8) ((temp & 0xFF00) >> 8);
    data[2] = (UINT8) ((temp & 0x10000) >> 16);
    nim_reg_write(dev, R5C_ACQ_CARRIER, data, 3);
}

static UINT8 nim_s3501_get_SNR_index(struct nim_device *dev)
{
    INT16 lpf_out16;
    INT16 agc2_ref5;
    INT32 snr_indx = 0;
    UINT8 data[2];
    UINT16 tdata[2];

    //CR45
    nim_reg_read(dev, R45_CR_LCK_DETECT, &data[0], 1);
    //CR46
    nim_reg_read(dev, R45_CR_LCK_DETECT + 0x01, &data[1], 1);

    tdata[0] = (UINT16) data[0];
    tdata[1] = (UINT16) (data[1] << 8);
    lpf_out16 = (INT16) (tdata[0] + tdata[1]);
    lpf_out16 /= (16 * 2);

    //CR07
    nim_reg_read(dev, R07_AGC1_CTRL, &data[0], 1);
    agc2_ref5 = (INT16) (data[0] & 0x1F);

    snr_indx = (lpf_out16 * agc2_ref5 / 21) - 27;

    if (snr_indx < 0)
        snr_indx = 0;
    else if (snr_indx > 176)
        snr_indx = 176;

    return snr_indx;
}

static INT32 nim_s3501_get_fft_result(struct nim_device *dev, UINT32 freq, UINT32 *start_adr)
{
    int i, index;
    int ave_energy, max_est, maxvalue;

    UINT8 data = 0x10;
    UINT8 lock = 200;
    struct nim_s3501_private *dev_priv;

    dev_priv = dev->priv;

    if (NULL == start_adr)
        return ERR_ADDRESS;
    //reset first
    nim_s3501_demod_ctrl(dev, NIM_DEMOD_CTRL_0X90);

    data = 0x35;
    nim_reg_write(dev, R0A_AGC1_LCK_CMD, &data, 1);

    if (nim_s3501_i2c_open(dev))
        return S3501_ERR_I2C_NO_ACK;

    dev_priv->nim_Tuner_Control(dev_priv->tuner_id, freq, 0);

    comm_sleep(10);
    dev_priv->nim_Tuner_Status(dev_priv->tuner_id, &lock);

    if (nim_s3501_i2c_close(dev))
        return S3501_ERR_I2C_NO_ACK;

    nim_s3501_calculate_energy(dev);
    nim_s3501_smoothfilter();

    comm_memcpy(start_adr, FFT_energy_1024, sizeof(FFT_energy_1024));

    start_adr[511] = (start_adr[510] + start_adr[512]) / 2;
    i = 0;ave_energy = 0;max_est = 0;
    for (index = 256; index <= 767; index++)
    {
        ave_energy += start_adr[index];
        if (i == (1 << (STATISTIC_LENGTH + 1)) - 1)
        {
            if (ave_energy > max_est)
                max_est = ave_energy;
            i = 0;
            ave_energy = 0;
        }
        else
        {
            i++;
        }
    }
    max_est = max_est / 8;
    maxvalue = max_est;

    for (index = 256; index <= 767; index++)
    {
        if ((start_adr[index] > (UINT32) maxvalue) && (start_adr[index] < (UINT32) (max_est * 2)))
            maxvalue = start_adr[index];
    }

    for (index = 256; index <= 767; index++)
    {
        if (start_adr[index] > (UINT32) maxvalue)
            start_adr[index] = maxvalue;
        start_adr[index] = start_adr[index] * 250 / maxvalue;
    }
    return SUCCESS;
}

INT32 nim_s3501_fft_find_channel(struct nim_device *dev, UINT32 *tune_freq)
{
    INT32 success;
    INT32 delta_fc_est[MAX_CH_NUMBER];
    INT32 SymbolRate_est[MAX_CH_NUMBER];
    INT32 IF_freq;
    INT32 ch_number;
    INT32 i;
    INT32 tune_jump_freq;
    INT32 tempFreq;
    INT32 temp, temp1;
    UINT8 data;
    UINT32 sample_rate, sample_tmp;

    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;


    if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
    {
        if ((priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C) && (priv->ul_status.m_tso_mode == 1))
        {
            // Need not configure TSO speed.
        }
        else if (priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_S3501D)
        {
            data = 0x40;    //capture fix to 8bit mode
            nim_reg_write(dev, RAD_TSOUT_SYMB + 0x01, &data, 1);
        }
        else
        {
            nim_reg_read(dev, RC0_BIST_LDPC_REG, &data, 1);
            data = data & 0x3f;
            data = data | 0x80; //8bit mode
            nim_reg_write(dev, RC0_BIST_LDPC_REG, &data, 1);
        }
    }
    else
    {
        data = 0x40; 
        nim_reg_write(dev, RAD_TSOUT_SYMB + 0x01, &data, 1);
    }

    nim_s3501_calculate_energy(dev);
    NIM_PRINTF("tune_freq = %d\n", *tune_freq);

    if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
    {
        if ((priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C) && (priv->ul_status.m_tso_mode == 1))
        {
            // Need not configure TSO speed.
        }
        else if (priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_S3501D)
        {
            if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_1BIT_MODE)
                data = 0xe0;
            else if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_2BIT_MODE)
                data = 0x00;
            else if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_4BIT_MODE)
                data = 0x20;
            else if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_8BIT_MODE)
                data = 0x40;
            else
                data = 0x40;
            nim_reg_write(dev, RAD_TSOUT_SYMB + 0x01, &data, 1);
        }
        else
        {
            nim_reg_read(dev, RC0_BIST_LDPC_REG, &data, 1);
            data = data & 0x3f;
            data = data | 0xc0; //1//enable 1bit mode
            nim_reg_write(dev, RC0_BIST_LDPC_REG, &data, 1);
        }
    }
    else
    {
        nim_s3501_set_ts_mode(dev, 0x0, 0x0, 0x0, 0x0, 0x1);
    }

       if(NIM_SCAN_SLOW == priv->blscan_mode)
       {
        comm_memcpy( fft_energy_store,FFT_energy_1024,sizeof(FFT_energy_1024));
        temp1 = fft_energy_store[511];
        fft_energy_store[511] = (fft_energy_store[510]+fft_energy_store[512])/2;
        for (i=6;i<1024-6;i++){
            temp = 0;
            for (data=0;data<12;data++)
                temp = temp + fft_energy_store[i-6+data];
            temp = temp/12;
            FFT_energy_1024[i] = temp;
        }
        fft_energy_store[511] = temp1;
       }

    nim_s3501_autosearch((INT32 *) &success, (INT32 *) delta_fc_est, (INT32 *) SymbolRate_est, (INT32 *) &IF_freq, (INT32 *) &ch_number);

    nim_s3501_get_dsp_clk(dev, &sample_rate);
    if (1 == success)
    {
        for (i = 0; i < ch_number; i++)
        {
            temp = nim_s3501_multu64div(delta_fc_est[i], sample_rate, 90000); //
            tempFreq = (*tune_freq)*1024 + temp;
            priv->ul_status.m_Freq[priv->ul_status.m_CRNum] = tempFreq;
            NIM_PRINTF("    m_Freq[%d] is %d\n", priv->ul_status.m_CRNum, priv->ul_status.m_Freq[priv->ul_status.m_CRNum]);
            priv->ul_status.m_Rs[priv->ul_status.m_CRNum] = nim_s3501_multu64div(SymbolRate_est[i], sample_rate, 90000);
            priv->ul_status.m_CRNum++;
        }
    }
    else
    {
        NIM_PRINTF("    Base band width is %d, ch_number is%d\n", IF_freq / 1024, ch_number);
    }
    IF_freq = nim_s3501_multu64div(IF_freq, sample_rate, 90000); //
    tune_jump_freq = IF_freq / 1024;
    return tune_jump_freq;
}

static INT32 nim_s3501_WidebandScan_open(struct nim_device *dev,UINT32 start_freq, UINT32 end_freq,UINT32 step_freq)
{
    UINT32 full_size=0;

    Last_Tuner_IF=0;
    chlspec_Num=0;
    called_num=0;

    full_size = (end_freq-start_freq)/step_freq;
    full_size = (full_size+2)*512;
    channel_spectrum=(INT32 *) comm_malloc(full_size * 4);
    if(channel_spectrum==NULL)
    {
        NIM_PRINTF("\n channel_spectrum--> no enough memory!\n");
        return ERR_NO_MEM;
    }

    if (nim_s3501_get_bypass_buffer(dev))
    {
        NIM_PRINTF("\n ADCdata--> no enough memory!\n");
        comm_free(channel_spectrum);
        return ERR_NO_MEM;
    }

    return SUCCESS;

}
static INT32 nim_s3501_WidebandScan_close()
{
    comm_free(channel_spectrum);
    return SUCCESS;
}
static INT32 nim_s3501_WidebandScan(struct nim_device *dev,UINT32 start_freq, UINT32 end_freq)
{
    UINT32 FFT_freq=0;
    UINT32 i=0,j=0;
    UINT32 ADC_sample_freq=0;
    UINT32  step_freq=0;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;

    NIM_PRINTF("Enter function nim_s3501_WidebandScan:\n");

    if(priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501A)  
        ADC_sample_freq=98;
    else
        ADC_sample_freq=((CRYSTAL_FREQ*90 + 6750)/13500);   
    step_freq=ADC_sample_freq/2;  

    if(SUCCESS == nim_s3501_WidebandScan_open(dev,start_freq,end_freq,step_freq))
    {
        for (FFT_freq = start_freq; FFT_freq < end_freq; FFT_freq += step_freq)
        {
            nim_s3501_demod_ctrl(dev, NIM_DEMOD_CTRL_0X91);
            comm_memset(FFT_energy_1024, 0, sizeof(FFT_energy_1024));
            for(j=0;j<100;j++)
            {
                nim_S3501_cap_start(dev, FFT_freq, 0, FFT_energy_1024);
                for(i=0;i<1024;i++)
                {
                    if(FFT_energy_1024[i]>0)
                        break;
                }
                if(i<1024)
                    break;
            }

            if(100 == j)
            {
                comm_free(priv->ul_status.ADCdata_malloc_addr);
                NIM_PRINTF("the nim_s3501_WidebandScan function  works wrong !\n");
                NIM_PRINTF("ERR_FAILURE, Leave the nim_s3501_WidebandScan function !\n");
                return ERR_FAILURE;
            }
            nim_s3501_FFT_WidebandScan(dev,FFT_freq,ADC_sample_freq);
        }
        comm_free(priv->ul_status.ADCdata_malloc_addr);
        NIM_PRINTF("SUCCESS, Leave the nim_s3501_WidebandScan function !\n");
        return SUCCESS;
    }
    else
    {
        NIM_PRINTF("ERR_NO_MEM, Leave the nim_s3501_WidebandScan function !\n");
        return ERR_NO_MEM;
    }
}

#define NIM_TS_PORT_CAP
UINT8 M3501_Debug_Flag=4;
void nim_s3501_calculate_energy(struct nim_device *dev)
{
    INT32 i, j, k, m,n;
    INT32 energy_real, energy_imag;
    INT32 energy;
    INT32 energy_tmp;
    INT32 fft_I, fft_Q;
    UINT8 data;
    UINT8 *pFFTdata, *pData = &data;
    UINT32 OldIDPos, Find11Packet, PacketNum;
    UINT32 NO_S3501ID;
    UINT32 overflow_cnt = 0;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    comm_memset(FFT_energy_1024, 0, sizeof(FFT_energy_1024));

#ifdef NIM_TS_PORT_CAP
    nim_s3501_fft_result_read(dev);
    pFFTdata = (UINT8 *) (priv->ul_status.ADCdata);
    comm_delay(100); 
#endif
    OldIDPos = 0;
    PacketNum = 0;

#ifdef   NIM_TS_PORT_CAP
    for (k = 0; k < 20; k++)
    {
        if (priv->ul_status.s3501_autoscan_stop_flag)
        {
            return;
        }

        for(n=0;n<1024;n++)
        {
            FFT_energy_1024_tmp[n] = 0;
        }
        Find11Packet = 0;
        while ((Find11Packet == 0) && (PacketNum < 662))
        {
            NO_S3501ID = 1;
            for (i = 0; i < 10; i++)
            {
                data = *(pFFTdata + i);
                if (S3501ID[i] != data)
                {
                    NO_S3501ID = 0;
                }
            }

            if (NO_S3501ID == 1)
            {
                if ((PacketNum - OldIDPos) == 11)
                {
                    pData = pFFTdata - 2068;
                    Find11Packet = 1;
                }
                OldIDPos = PacketNum;
            }
            pFFTdata = pFFTdata + 188;
            PacketNum++;
        }
        if (Find11Packet == 0)
        {
            NIM_PRINTF("cannot find 20 complete S3501 packet\n");

            for(i=0;i<1024;i++)
                FFT_energy_1024[i] =0;
            return;
        }
        comm_memcpy(priv->ul_status.ADCdata, (pData + 10), 178);
        for (i = 0; i < 10; i++)
        {
            comm_memcpy(priv->ul_status.ADCdata + 178 + i * 187, pData + (i + 1) * 188 + 1, 187);
        }
        overflow_cnt = 0;
        for (i = 0; i < 1024; i++)
        {
            fft_I = priv->ul_status.ADCdata[2 * i];
            fft_Q = priv->ul_status.ADCdata[2 * i + 1];

            if ((fft_I==127) ||(fft_I==128))
                overflow_cnt++;
            if ((fft_Q==127) || (fft_Q==128))
                overflow_cnt++;

            if (fft_I & 0x80)
                fft_I |= 0xffffff00;
            if (fft_Q & 0x80)
                fft_Q |= 0xffffff00;

            m = 0;for (j = 0; j < 10; j++)
            {
                    m <<= 1;m += ((i >> j) & 0x1);
            }
            fft_I = fft_I << (FFT_BITWIDTH - 8);
            fft_Q = fft_Q << (FFT_BITWIDTH - 8);
            priv->ul_status.FFT_I_1024[m] = fft_I;
            priv->ul_status.FFT_Q_1024[m] = fft_Q;
        }
if(1==M3501_Debug_Flag&(0x01))
{
	for(i = 0; i< 1024; i++)
	{
		AUTOSCAN_PRINTF("ADCdata%d:[%d,%d]\n",i,priv->ul_status.FFT_I_1024[i],priv->ul_status.FFT_Q_1024[i]);
	}
}
        //R2FFT
        R2FFT(priv->ul_status.FFT_I_1024, priv->ul_status.FFT_Q_1024);

#else

        for (k = 0; k < 20; k++)
        {
            if (priv->ul_status.s3501_autoscan_stop_flag)
            {
                return;
            }
            NIM_PRINTF("fft_start\n");
            nim_s3501_fft_result_read(dev);
            NIM_PRINTF("fft_end\n");
#endif
        for (i = 0; i < 1024; i++)
        {
            fft_I = priv->ul_status.FFT_I_1024[i];
            fft_Q = priv->ul_status.FFT_Q_1024[i];

            energy_real = fft_I * fft_I;
            energy_imag = fft_Q * fft_Q;
            energy = energy_real + energy_imag;
            energy >>= 3;
            j = (i + 511) & 1023;
            energy_tmp =  energy;

            if ((energy_tmp >> 20) & 1)
            {
                FFT_energy_1024_tmp[j] = 1048575;
            }
            else
            {
                FFT_energy_1024_tmp[j] = energy_tmp;
            }
        }
        for(n=0;n<1024;n++)
        {
            FFT_energy_1024[n] += FFT_energy_1024_tmp[n];
        }

    }
}

void nim_s3501_fft_result_read(struct nim_device *dev)
{
    UINT8 data = 0;
    UINT8 read_data = 0;
    INT32 m = 0;

    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;

    nim_s3501_demod_ctrl(dev, NIM_DEMOD_CTRL_0X91);

    nim_s3501_set_dsp_clk (dev, 0);

    nim_s3501_adc_setting(dev);

    nim_s3501_interrupt_mask_clean(dev);

    if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
    {
        if((priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C) && (priv->ul_status.m_tso_mode == 1))
        {
            data = 0x11;
            nim_reg_write(dev, RF0_HW_TSO_CTRL, &data, 1);
        }
    }
    //CR07
    nim_s3501_agc1_ctrl(dev, 0x00, NIM_OPTR_FFT_RESULT);

#ifdef NIM_TS_PORT_CAP

    nim_s3501_demod_ctrl(dev, NIM_DEMOD_CTRL_0X02);

    data = 0x3c;    //0xfc lead lots of tps.
    nim_reg_write(dev, RBD_CAP_PRM + 0x01, &data, 1); 

#else

    //CRBE
    data = 0x01;
    nim_reg_write(dev, RBD_CAP_PRM + 0x01, &data, 1);
#endif
    //CR00

    data = 0x55; 
    nim_reg_write(dev, R0A_AGC1_LCK_CMD, &data, 1);

    nim_s3501_demod_ctrl(dev, NIM_DEMOD_CTRL_0X52);

    comm_delay(1000);
    memset(priv->ul_status.ADCdata_malloc_addr,0,0x20000 * 2);

    dmx_pause((struct dmx_device *)dev_get_by_id(HLD_DEV_TYPE_DMX, 0));
    nim_s3501_autoscan_signal_input(dev,NIM_SIGNAL_INPUT_OPEN);

    data = 0x50; 
    nim_reg_write(dev, R0A_AGC1_LCK_CMD, &data, 1);

    for (m = 0; m < 1000; m++)
    {
        comm_delay(1000);
        nim_reg_read(dev, R02_IERR, &read_data, 1);
        if (0x01 == (read_data & 0x01))
        {
#ifdef NIM_TS_PORT_CAP
            NIM_PRINTF(" ts data transfer finish\n");
            break;
#else

            nim_s3501_cap_IQ_enerage(dev);

            //R2FFT
            R2FFT(priv->ul_status.FFT_I_1024, priv->ul_status.FFT_Q_1024);
            NIM_PRINTF(" ADC DATA  transfer finish\n");
            break;

#endif
        }
    }
    nim_s3501_autoscan_signal_input(dev,NIM_SIGNAL_INPUT_CLOSE);
    dmx_start((struct dmx_device *)dev_get_by_id(HLD_DEV_TYPE_DMX, 0));
}

#ifdef NIM_CAPTURE_SUPPORT

INT32 nim_S3501_cap_start(struct nim_device *dev, UINT32 startFreq, UINT32 sym, UINT32 *cap_buffer)
{
    INT32 i;
    UINT32  freq = 0;
    UINT8   data = 0x10;
    UINT8   low_sym = 0;
    INT32   result = 200;
    UINT8   lock = 200;
    UINT8 ver_data;
    struct nim_s3501_private *dev_priv;

    dev_priv = dev->priv;

    nim_s3501_agc1_ctrl(dev,low_sym,NIM_OPTR_CHL_CHANGE);

    NIM_PRINTF("Enter nim_S3501_cap_start:\n");

    freq = startFreq;

    if(nim_s3501_i2c_open(dev))
        return ERR_I2C_NO_ACK;

    freq = startFreq;
    result = dev_priv->nim_Tuner_Control(dev_priv->tuner_id,freq, sym);
    if (result==SUCCESS)
        NIM_PRINTF("Config tuner %dMHz success\n",freq);
    else
    {
        NIM_PRINTF("Config tuner %dMHz failed\n",freq);
    }

    lock=0;
    for( i=0; i<1000; i++ )
    {
        comm_delay(1000);
        result = dev_priv->nim_Tuner_Status(dev_priv->tuner_id,&lock);
        if (lock)
	{
            NIM_PRINTF("Tuner lock after %d times delay\n",i+1);
            break;
        }
    }
    if(lock==0)
    {
        NIM_PRINTF("Tuner unlock at %dMHz\n",freq);
    }

    if(nim_s3501_i2c_close(dev))
            return ERR_I2C_NO_ACK;

    nim_s3501_adc_setting(dev);
    //CR07
    nim_s3501_agc1_ctrl(dev,low_sym,NIM_OPTR_CHL_CHANGE);

    nim_reg_read(dev,R07_AGC1_CTRL,&ver_data,1);
    if(dev_priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
    {
        if ((dev_priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C) && (dev_priv->ul_status.m_tso_mode == 1))
        {
            // Need not configure TSO speed.
        }
        else if(dev_priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_S3501D)
        {
            data = 0x40;    //capture fix to 8bit mode
            nim_reg_write(dev,RAD_TSOUT_SYMB+0x01, &data, 1);
        }
        else
        {
            nim_reg_read(dev, RC0_BIST_LDPC_REG, &data, 1);
            data = data & 0x3f;
            data = data | 0x80; //8bit mode
            nim_reg_write(dev, RC0_BIST_LDPC_REG, &data, 1);
	     nim_m3501c_fec_ts_on(dev);
	     nim_m3501_ts_on(dev);
        }
    }
    else
    {
        data = 0x40; //M3501A need config this for SPI_CLK output
        nim_reg_write(dev,RAD_TSOUT_SYMB+0x01, &data, 1);
    }

       nim_s3501_calculate_energy(dev);

    if(dev_priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
    {
        if ((dev_priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C) && (dev_priv->ul_status.m_tso_mode == 1))
        {
            // Need not configure TSO speed.
        }
        else if(dev_priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_S3501D)
        {
            if((dev_priv->Tuner_Config_Data.QPSK_Config&0xc0)==M3501_1BIT_MODE)
                data=0xe0;
            else if((dev_priv->Tuner_Config_Data.QPSK_Config&0xc0)==M3501_2BIT_MODE)
                data=0x00;
            else if((dev_priv->Tuner_Config_Data.QPSK_Config&0xc0)==M3501_4BIT_MODE)
                data=0x20;
            else if((dev_priv->Tuner_Config_Data.QPSK_Config&0xc0)==M3501_8BIT_MODE)
                data=0x40;
            else
                data=0x40;
            nim_reg_write(dev,RAD_TSOUT_SYMB+0x01, &data, 1);
        }
        else
        {
            nim_reg_read(dev, RC0_BIST_LDPC_REG, &data, 1);
            data = data & 0x3f;
            data = data | 0xc0; //1//enable 1bit mode
            nim_reg_write(dev, RC0_BIST_LDPC_REG, &data, 1);
        }
    }
    else
    {
        nim_s3501_set_ts_mode(dev, 0x0, 0x0, 0x0, 0x0,0x1);
    }

    return SUCCESS;
}

INT32 nim_S3501_cap(struct nim_device *dev, UINT32 startFreq, UINT32 *cap_buffer, UINT32 sym)
{
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;

    NIM_PRINTF("Enter nim_S3501_cap:\n");
    if (nim_s3501_get_bypass_buffer(dev))
        return ERR_NO_MEM;
    nim_s3501_autoscan_signal_input(dev,NIM_SIGNAL_INPUT_OPEN);
    nim_S3501_cap_start(dev, startFreq, sym, cap_buffer);
    nim_s3501_autoscan_signal_input(dev,NIM_SIGNAL_INPUT_CLOSE);
    comm_free(priv->ul_status.ADCdata_malloc_addr);
    return SUCCESS;
}

#endif


static INT32 nim_s3501_get_bypass_buffer(struct nim_device *dev)
{
    // According to DMX IC spec, bypass buffer must locate in 8MB memory segment.
    // Bypass buffer size is (BYPASS_BUFFER_REG_SIZE * 188)
#define BYPASS_BUF_SIZE 0x20000     // 128 KB
#define BYPASS_BUF_MASK (BYPASS_BUF_SIZE - 1)
    UINT32 tmp;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;

    priv->ul_status.ADCdata_malloc_addr=(UINT8 *) comm_malloc(BYPASS_BUF_SIZE * 2);
    if (priv->ul_status.ADCdata_malloc_addr == NULL)
        return ERR_NO_MEM;

    NIM_PRINTF("ADCdata=0x%08x\n", priv->ul_status.ADCdata_malloc_addr);

    comm_memset(priv->ul_status.ADCdata_malloc_addr, 0, BYPASS_BUF_SIZE * 2);
    tmp = ((UINT32) (priv->ul_status.ADCdata_malloc_addr)) & BYPASS_BUF_MASK;
    if (tmp)
        priv->ul_status.ADCdata = priv->ul_status.ADCdata_malloc_addr + BYPASS_BUF_SIZE - tmp;
    else
        priv->ul_status.ADCdata = priv->ul_status.ADCdata_malloc_addr;

    return SUCCESS;
}


#define AUTOSCAN_FULL_SPECTRUM
#ifdef AUTOSCAN_FULL_SPECTRUM

static INT32 nim_s3501_autoscan(struct nim_device *dev, struct NIM_Auto_Scan *pstAuto_Scan)
{
    UINT8 cur_fec=0;  
    UINT32 cur_freq=0, cur_sym=0; 
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    INT32 i=0,j=0, ret = SUCCESS; 
    INT32 crnum;
    UINT32 FFT_freq, step_freq = 0;
    UINT32 FFT_result;
    UINT8 data;  
    UINT8 chcnt,double_i=0;
    UINT32 temp,temp_step=0;
    UINT32 sym,freq,last_lock_rs;
    INT32 success;
    UINT32 last_lock_freq=0;
    INT32 temp_freq=0;
    struct nim_s3501_private *dev_priv;
    dev_priv = dev->priv;
    UINT32 ADC_sample_freq=0;
    UINT32 start_t,end_t,temp_t;
    INT32 Bug_Freq[2];
    INT32 Bug_Sym[2];
    INT32 Scan_mode=0;
    INT32 loop=1;
    INT32 loop_index=0;

    NIM_PRINTF("Enter nim_s3501_autoscan:\n");
    AUTOSCAN_PRINTF("Enter nim_s3501_autoscan:sfreq = %d, efreq = %d\n", pstAuto_Scan->sfreq, pstAuto_Scan->efreq);
    
     last_tp_freq=0;
    start_t=OS_GetTickCount();
    
    #ifdef AUTOSCAN_DEBUG
    config_data = priv->Tuner_Config_Data.QPSK_Config;
    priv->Tuner_Config_Data.QPSK_Config |= M3501_SIGNAL_DISPLAY_LIN;
    #endif

#ifdef CHANNEL_CHANGE_ASYNC
    UINT32 flag_ptn = 0;
    if(NIM_FLAG_WAIT(&flag_ptn, priv->flag_id, NIM_FLAG_CHN_CHG_START|NIM_FLAG_CHN_CHANGING,OSAL_TWF_ORW, 0) == OSAL_E_OK)
    {
        priv->ul_status.s3501_chanscan_stop_flag = 1;
        comm_sleep(10);
    }
#endif

    if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
        priv->tsk_status.m_lock_flag = NIM_LOCK_STUS_NORMAL;

    if (pstAuto_Scan->sfreq <AS_FREQ_MIN || pstAuto_Scan->efreq> AS_FREQ_MAX || pstAuto_Scan->sfreq > pstAuto_Scan->efreq)
    {
        return ERR_FAILUE;
    }
    nim_s3501_ioctl(dev, NIM_DRIVER_STOP_ATUOSCAN, 0);

    //-------- soft search configure
    data = 0x1f; 
    nim_reg_write(dev, R1B_TR_TIMEOUT_BAND, &data, 1);
    nim_s3501_cr_setting(dev, NIM_OPTR_SOFT_SEARCH);

    if(CRYSTAL_FREQ > 15000)
        data = 0xf3;
    else
        data = 0x73;
    nim_reg_write(dev, R5B_ACQ_WORK_MODE, &data, 1);

    nim_s3501_TR_CR_Setting(dev, NIM_OPTR_SOFT_SEARCH);
    nim_s3501_ldpc_setting(dev, NIM_OPTR_SOFT_SEARCH, 0x00, 0x01);

    nim_s3501_hbcd_timeout(dev, NIM_OPTR_SOFT_SEARCH);

    nim_reg_read(dev, RDC_EQ_DBG_TS_CFG, &data, 1);
    data = data & 0xef;
    nim_reg_write(dev, RDC_EQ_DBG_TS_CFG, &data, 1);

    if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
    {
        if((priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C) && (priv->ul_status.m_tso_mode == 1))
        {
#ifdef C3501C_ERRJ_LOCK
            nim_m3501c_recover_moerrj(dev);
#endif
            nim_m3501c_close_dummy(dev);
        }
        else if ((priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_S3501D))
        {
            //S3501D
            if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_1BIT_MODE)
            {
                data = 0x06; // symbol period from reg, 2 cycle
                nim_reg_write(dev, RAD_TSOUT_SYMB, &data, 1);
                NIM_PRINTF("open ci plus enable REG_ad = %02x \n", data);

                nim_reg_read(dev, RAD_TSOUT_SYMB + 0x01, &data, 1);
                data = data | 0x80;    // enable symbol period from reg
                nim_reg_write(dev, RAD_TSOUT_SYMB + 0x01, &data, 1);

                nim_reg_read(dev, RDC_EQ_DBG_TS_CFG, &data, 1);
                data = data | 0xe0;
                nim_reg_write(dev, RDC_EQ_DBG_TS_CFG, &data, 1);

                data = 0x1d;  //use 98M clk and enable ci+
                nim_reg_write(dev, RDF_TS_OUT_DVBS2, &data, 1);

                data = 0x0f;
                nim_reg_write(dev, RD8_TS_OUT_SETTING, &data, 1);
            }
            else
            {
                //for S3501D 2bit/4bit/8bit auto search, close dummy.
                nim_close_ts_dummy(dev);
            }
        }
        else
        {
            if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_8BIT_MODE)
            {
                nim_close_ts_dummy(dev);
            }
            else
            {
                //open CI+ first.
                data = 0x02;    // symbol period from reg, 2 cycle
                nim_reg_write(dev, RAD_TSOUT_SYMB, &data, 1);
                NIM_PRINTF("open ci plus enable REG_ad = %02x \n", data);

                nim_reg_read(dev, RAD_TSOUT_SYMB + 0x01, &data, 1);
                data = data | 0x80;    // enable symbol period from reg
                nim_reg_write(dev, RAD_TSOUT_SYMB + 0x01, &data, 1);

                nim_reg_read(dev, RDC_EQ_DBG_TS_CFG, &data, 1);
                data = data | 0xe0;
                nim_reg_write(dev, RDC_EQ_DBG_TS_CFG, &data, 1);

                data = 0x1d;  //use 98M clk and enable ci+
                nim_reg_write(dev, RDF_TS_OUT_DVBS2, &data, 1);

                nim_reg_read(dev, RD8_TS_OUT_SETTING, &data, 1);
                data = data & 0x08 | 0x07;
                nim_reg_write(dev, RD8_TS_OUT_SETTING, &data, 1);

                if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_2BIT_MODE)
                {
                    //ECO_SSI_2B_EN = cr9f[3]
                    nim_reg_read(dev, R9C_DEMAP_BETA + 0x03, &data, 1);
                    data = data | 0x08;
                    nim_reg_write(dev, R9C_DEMAP_BETA + 0x03, &data, 1);

                    //ECO_SSI_SEL_2B = crc0[3]
                    nim_reg_read(dev, RC0_BIST_LDPC_REG, &data, 1);
                    data = data | 0x08; 
                    nim_reg_write(dev, RC0_BIST_LDPC_REG, &data, 1);
                }
                else if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_4BIT_MODE)
                {
                    //ECO_SSI_2B_EN = cr9f[3]
                    nim_reg_read(dev, R9C_DEMAP_BETA + 0x03, &data, 1);
                    data = data | 0x08;
                    nim_reg_write(dev, R9C_DEMAP_BETA + 0x03, &data, 1);

                    //ECO_SSI_SEL_2B = crc0[3]
                    nim_reg_read(dev, RC0_BIST_LDPC_REG, &data, 1);
                    data = data & 0xf7; 
                    nim_reg_write(dev, RC0_BIST_LDPC_REG, &data, 1);
                }
            }
            //open eco_ts_en
            nim_m3501c_fec_ts_on(dev);
            nim_m3501_ts_on(dev);
        }//end M3501B
    }

    Last_Tuner_IF=0;
    chlspec_Num=0;
    called_num=0;
    Max_FFT_energy=0;
	
    channel_spectrum=(INT32 *) comm_malloc(FS_MAXNUM * 4);
    if(channel_spectrum==NULL)
    {
        AUTOSCAN_PRINTF("\n channel_spectrum--> no enough memory!\n");
        return ERR_NO_MEM;
    }
	
    channel_spectrum_tmp=(INT32 *) comm_malloc(FS_MAXNUM * 4);
    if(channel_spectrum_tmp==NULL)
    {
        AUTOSCAN_PRINTF("\n channel_spectrum_tmp--> no enough memory!\n");
        comm_free(channel_spectrum);
        return ERR_NO_MEM;
    }

    if (nim_s3501_get_bypass_buffer(dev))
    {
        AUTOSCAN_PRINTF("\n ADCdata--> no enough memory!\n");
        comm_free(channel_spectrum);
        comm_free(channel_spectrum_tmp);
        return ERR_NO_MEM;
    }

    if(priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501A)   //M3501A's sample clock is 98M, and crystal frequency is 13.5M .
        ADC_sample_freq=98;
    else
	ADC_sample_freq=((CRYSTAL_FREQ*90+6750)/13500);   //M3501B/C need to surpport both 13.5M and 16M crystal.
      ADC_sample_freq=ADC_sample_freq-(ADC_sample_freq%2);
     AUTOSCAN_PRINTF("=========>>ADC_sample_freq=%d\n",ADC_sample_freq);
    step_freq=ADC_sample_freq/2;  
 //    step_freq=(ADC_sample_freq+2)/4;  
    for (FFT_freq = pstAuto_Scan->sfreq; FFT_freq < pstAuto_Scan->efreq; FFT_freq += step_freq)
    {
        nim_s3501_demod_ctrl(dev, NIM_DEMOD_CTRL_0X91);
        comm_memset(FFT_energy_1024, 0, sizeof(FFT_energy_1024));
        while(1)
        {
            nim_S3501_cap_start(dev, FFT_freq, 0, FFT_energy_1024);
            for(i=0;i<1024;i++)
            {
                if(FFT_energy_1024[i]>0)
                    break;
            }
            if(i<1024)
                break;

            if(dev_priv->ul_status.s3501_autoscan_stop_flag)
            {
                comm_free(channel_spectrum);
                comm_free(channel_spectrum_tmp);
                comm_free(priv->ul_status.ADCdata_malloc_addr);
                AUTOSCAN_PRINTF("nim_s3501_autoscan:stop_flag=1\n");
                return SUCCESS;
            }
        }
        called_num++;
        if(1==((M3501_Debug_Flag>>1)&0x01))
        {
            AUTOSCAN_PRINTF("call_num=%d,Tuner_IF=%d \n",called_num,FFT_freq);
            AUTOSCAN_PRINTF("FFT1024-> 0000 : 1000000 \n");
            for(i=0;i<1024;i++)
                AUTOSCAN_PRINTF("FFT1024-> %4d : %d \n",i,FFT_energy_1024[i]);
        }

        if(NIM_SCAN_SLOW == priv->blscan_mode)
        {
            Scan_mode=1;
        }
        else
        {
            Scan_mode=0;
        }
        nim_s3501_MedianFilter(1024,FFT_energy_1024,Scan_mode); // Median filter for restrain single tone noise
        nim_s3501_FFT_WidebandScan(dev,FFT_freq,ADC_sample_freq);

        if(dev_priv->ul_status.s3501_autoscan_stop_flag)
        {
            comm_free(channel_spectrum);
            comm_free(channel_spectrum_tmp);
            comm_free(priv->ul_status.ADCdata_malloc_addr);
            AUTOSCAN_PRINTF("nim_s3501_autoscan:stop_flag=1\n");
            return SUCCESS;
        }
    }

    comm_free(priv->ul_status.ADCdata_malloc_addr);
    end_t=OS_GetTickCount();
    temp_t=end_t;
    AUTOSCAN_PRINTF("\tWideband FFT cost time %dms\n",end_t-start_t);
    if(1==((M3501_Debug_Flag>>2)&0x01))
    {
    for(i=0;i<chlspec_Num;i++)
             AUTOSCAN_PRINTF("FFT_WBS-->%d : %d\n",i,channel_spectrum[i]);
    }
    end_t=OS_GetTickCount();
    AUTOSCAN_PRINTF("\tPrint FFT result cost time %dms\n",end_t-temp_t);
    temp_t=end_t;

    AUTOSCAN_PRINTF("Max_FFT_energy=%d\n",Max_FFT_energy);
    loop=1;
    if(Max_FFT_energy>8388607)
    {
        loop=2;
    }
    AUTOSCAN_PRINTF("************loop=%d****************\n",loop);
    if(loop==2)
    {
        for(i=0;i<chlspec_Num;i++)
        {
            channel_spectrum_tmp[i]=channel_spectrum[i]/(8388607>>5);
            if(channel_spectrum_tmp[i]<=0)
                channel_spectrum_tmp[i]=1;
            else if(channel_spectrum_tmp[i]>8388607)
                channel_spectrum_tmp[i]=8388607;

            if (channel_spectrum[i]>8388607)    // max 23bit = 0x7fffff
                channel_spectrum[i]=8388607;
            else if(channel_spectrum[i]==0) 
                channel_spectrum[i]=1;
        }
    }

    //autosearch
    TP_number=0;
    comm_memset(Frequency_Est, 0, sizeof(Frequency_Est));
    comm_memset(SymbolRate_Est, 0, sizeof(SymbolRate_Est));
    //nim_s3501_SearchTP(&success, Frequency_Est, SymbolRate_Est, &TP_number, chlspec_Num, channel_spectrum,pstAuto_Scan->sfreq,ADC_sample_freq);
    for(loop_index=0;loop_index<loop;loop_index++)
    {
        if(loop_index==0)
        {
            nim_s3501_SearchTP(chlspec_Num, channel_spectrum, pstAuto_Scan->sfreq, ADC_sample_freq,loop_index);
            AUTOSCAN_PRINTF("Time %d : Find TP number is %d\n",loop_index,TP_number);
        }
        else if(loop_index==1)
        {
            nim_s3501_SearchTP(chlspec_Num, channel_spectrum_tmp, pstAuto_Scan->sfreq, ADC_sample_freq,loop_index);
            AUTOSCAN_PRINTF("time %d : Find TP number is %d\n",loop_index,TP_number);
        }
    }
    comm_free(channel_spectrum);
    comm_free(channel_spectrum_tmp);
    end_t=OS_GetTickCount();
    AUTOSCAN_PRINTF("\tSearch TP cost time %dms\n",end_t-temp_t);

#ifdef DEBUG_SOFT_SEARCH
    success=0;
    TP_number=1000;
    Bug_Freq[0]=1887904;
    Bug_Sym[0]=1875;
    Bug_Freq[1]=1762288;
    Bug_Sym[1]=1968;
    comm_memset(Frequency_Est, 0, sizeof(Frequency_Est));
    comm_memset(SymbolRate_Est, 0, sizeof(SymbolRate_Est));
    for(i=0;i<TP_number;i++)
    {
        Frequency_Est[i]=Bug_Freq[i%2];
        SymbolRate_Est[i]=Bug_Sym[i%2];
    }
#endif

    if(TP_number>0)   
        success=0;
    AUTOSCAN_PRINTF("\success = %d , TP_number = %d  \n",success,TP_number);
    if (success==0)
    {
        last_lock_freq = 0;
        last_lock_rs = 0;
	Final_Est_Freq=0;
	Final_Est_SymRate=0;
        for (i=0;i<TP_number;i++)
        {
            AUTOSCAN_PRINTF("\n\n----> Try TP [freq,Sym]=[%d, %d]\n",Frequency_Est[i],SymbolRate_Est[i]);
            if (dev_priv->ul_status.s3501_autoscan_stop_flag)
            {
                AUTOSCAN_PRINTF("nim_s3501_autoscan:stop_flag=1\n");
                return SUCCESS;
            }

#ifdef DEBUG_SOFT_SEARCH
            priv->ul_status.m_CRNum = 1;
            priv->ul_status.m_Freq[0] = Frequency_Est[i];
            priv->ul_status.m_Rs[0] = SymbolRate_Est[i];
#else
            if(Frequency_Est[i]/1024<945)
                continue;
            if(Frequency_Est[i]/1024>2155)
                continue;
            temp_t=OS_GetTickCount();
            temp_freq = (Frequency_Est[i]+512)/1024-last_lock_freq;
            if(temp_freq==0)    continue;
            if (temp_freq<0)
                temp_freq = -temp_freq;
            if ((SymbolRate_Est[i]+1000)/2000>temp_freq)
                continue;
            if ((last_lock_rs+1000)/2000>(UINT32)temp_freq)
                continue;
            priv->ul_status.m_CRNum = 1;
            priv->ul_status.m_Freq[0] = Frequency_Est[i];
            priv->ul_status.m_Rs[0] = SymbolRate_Est[i];
#endif
            
            if (SUCCESS == nim_s3501_channel_search(dev, 0))
            {
                nim_s3501_get_code_rate(dev, &cur_fec);
                cur_freq=Final_Est_Freq;
                cur_sym=Final_Est_SymRate;
                last_lock_freq = cur_freq;
                last_lock_rs = cur_sym;
                if ((cur_freq >= pstAuto_Scan->sfreq) && (cur_freq <= pstAuto_Scan->efreq))
                {
                    //>>> Unicable begin
                    if (pstAuto_Scan->unicable) //cur_freq -> [950, 2150]
                    {
                        AUTOSCAN_PRINTF("\tUnicable FFT_freq: %d, cur_freq: %d, Fub: %d\n", FFT_freq, cur_freq, pstAuto_Scan->Fub);
                        ret = pstAuto_Scan->callback(1, 0, FFT_freq + cur_freq - pstAuto_Scan->Fub, cur_sym, cur_fec);
                    }
                    else    //<<< Unicable end
                    {
                        if((cur_freq>last_tp_freq-2)&&(cur_freq<last_tp_freq+2))
                        {
                            ret = pstAuto_Scan->callback(0, 0, Frequency_Est[i]/1024, 0, 0);
                            AUTOSCAN_PRINTF("\tRescan a tp: FFT_freq: %d, last_tp_freq: %d\n", Frequency_Est[i]/1024, last_tp_freq);
                        }
                        else
                        {
                            ret = pstAuto_Scan->callback(1, 0, cur_freq, cur_sym, cur_fec);
                            last_tp_freq=cur_freq;
                        }

                    }
                }
                else
                {
                    if (dev_priv->ul_status.s3501_autoscan_stop_flag)
                    {
                        AUTOSCAN_PRINTF("nim_s3501_autoscan:stop_flag=1\n");
                        return SUCCESS;
                    }
                    else
                    {
                        continue ;
                    }
                }
            }
            else
            {
                ret = pstAuto_Scan->callback(0, 0, Frequency_Est[i]/1024, 0, 0);
            }
            if (ret == 2)
            {
                return SUCCESS;
            }
            else if (ret == 1)
            {
                goto nim_as_break;
            }
            end_t=OS_GetTickCount();
            AUTOSCAN_PRINTF("\tTP %d cost time %dms\n",i,end_t-temp_t);
        }
    }

#ifdef AUTOSCAN_DEBUG
    priv->Tuner_Config_Data.QPSK_Config = config_data;
#endif

    end_t=OS_GetTickCount();
    AUTOSCAN_PRINTF("\tautoscan cost time   %d:%d   \n",(((end_t-start_t)+500)/1000/60),(((end_t-start_t)+500)/1000-(((end_t-start_t)+500)/1000/60)*60));
    NIM_PRINTF("Leave nim_s3501_autoscan\n");

    return SUCCESS;

    nim_as_break:

    pstAuto_Scan->callback(2, 0, 0, 0, 0);  /* Tell callback search finished */

    nim_s3501_autoscan_signal_input(dev, NIM_SIGNAL_INPUT_CLOSE);

    return ret;
}
#else
static INT32 nim_s3501_autoscan(struct nim_device *dev, struct NIM_Auto_Scan *pstAuto_Scan)
{
    UINT8 cur_fec;  
    UINT32 cur_freq, cur_sym; 
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    INT32 i, ret = SUCCESS; 
    INT32 crnum;
    UINT32 FFT_freq, step_freq = 0;
    UINT32 FFT_result;
    UINT8 data; 
    UINT8 chcnt,double_i=0;
    UINT32 temp,temp_step=0;
    UINT32 sym,freq;
    struct nim_s3501_private *dev_priv;
    dev_priv = dev->priv;

    last_ch_fc=0;
    last_tp_freq=0;

    AUTOSCAN_PRINTF("\napi_nim_autoscan: Begin autoscan\n");

    #ifdef NIM_S3501_ASCAN_TOOLS
        //pstAuto_Scan->sfreq=vs_s3501_ascan.va_ascan_g6_scan_f_start;
        //pstAuto_Scan->efreq=vs_s3501_ascan.va_ascan_g6_scan_f_end;
    #endif
    AUTOSCAN_PRINTF("\tsfreq = %d, efreq = %d\n", pstAuto_Scan->sfreq, pstAuto_Scan->efreq);
    #ifdef AUTOSCAN_DEBUG
    config_data = priv->Tuner_Config_Data.QPSK_Config;
    priv->Tuner_Config_Data.QPSK_Config |= M3501_SIGNAL_DISPLAY_LIN;
    #endif

#ifdef CHANNEL_CHANGE_ASYNC
    UINT32 flag_ptn = 0;
    if(NIM_FLAG_WAIT(&flag_ptn, priv->flag_id, NIM_FLAG_CHN_CHG_START|NIM_FLAG_CHN_CHANGING,OSAL_TWF_ORW, 0) == OSAL_E_OK)
    {
        priv->ul_status.s3501_chanscan_stop_flag = 1;
        comm_sleep(10);
    }
#endif

    if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
        priv->tsk_status.m_lock_flag = NIM_LOCK_STUS_NORMAL;

    if (pstAuto_Scan->sfreq <AS_FREQ_MIN || pstAuto_Scan->efreq> AS_FREQ_MAX || pstAuto_Scan->sfreq > pstAuto_Scan->efreq)
    {
        return ERR_FAILUE;
    }
    nim_s3501_ioctl(dev, NIM_DRIVER_STOP_ATUOSCAN, 0);

    if (nim_s3501_get_bypass_buffer(dev))
        return ERR_NO_MEM;

    //-------- soft search configure
    data = 0x1f;
    nim_reg_write(dev, R1B_TR_TIMEOUT_BAND, &data, 1);
    nim_s3501_cr_setting(dev, NIM_OPTR_SOFT_SEARCH);

    if(CRYSTAL_FREQ > 15000)
        data = 0xf3;
    else
        data = 0x73;
    nim_reg_write(dev, R5B_ACQ_WORK_MODE, &data, 1);

    nim_s3501_TR_CR_Setting(dev, NIM_OPTR_SOFT_SEARCH);
    nim_s3501_ldpc_setting(dev, NIM_OPTR_SOFT_SEARCH, 0x00, 0x01);

    nim_s3501_hbcd_timeout(dev, NIM_OPTR_SOFT_SEARCH);

    nim_reg_read(dev, RDC_EQ_DBG_TS_CFG, &data, 1);
    data = data & 0xef;
    nim_reg_write(dev, RDC_EQ_DBG_TS_CFG, &data, 1);

    if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
    {
        if((priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C) && (priv->ul_status.m_tso_mode == 1))
        {
#ifdef C3501C_ERRJ_LOCK
            nim_m3501c_recover_moerrj(dev);
#endif
            nim_m3501c_close_dummy(dev);
        }
        else if ((priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_S3501D))
        {
            //S3501D
            if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_1BIT_MODE)
            {
                data = 0x06; // symbol period from reg, 2 cycle
                nim_reg_write(dev, RAD_TSOUT_SYMB, &data, 1);
                NIM_PRINTF("open ci plus enable REG_ad = %02x \n", data);

                nim_reg_read(dev, RAD_TSOUT_SYMB + 0x01, &data, 1);
                data = data | 0x80;    // enable symbol period from reg
                nim_reg_write(dev, RAD_TSOUT_SYMB + 0x01, &data, 1);

                nim_reg_read(dev, RDC_EQ_DBG_TS_CFG, &data, 1);
                data = data | 0xe0;
                nim_reg_write(dev, RDC_EQ_DBG_TS_CFG, &data, 1);

                data = 0x1d;  //use 98M clk and enable ci+
                nim_reg_write(dev, RDF_TS_OUT_DVBS2, &data, 1);

                data = 0x0f;
                nim_reg_write(dev, RD8_TS_OUT_SETTING, &data, 1);
            }
            else
            {
                //for S3501D 2bit/4bit/8bit auto search, close dummy.
                nim_close_ts_dummy(dev);
            }
        }
        else
        {
            if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_8BIT_MODE)
            {
                nim_close_ts_dummy(dev);
            }
            else
            {
                //open CI+ first.
                data = 0x02;    // symbol period from reg, 2 cycle
                nim_reg_write(dev, RAD_TSOUT_SYMB, &data, 1);
                NIM_PRINTF("open ci plus enable REG_ad = %02x \n", data);

                nim_reg_read(dev, RAD_TSOUT_SYMB + 0x01, &data, 1);
                data = data | 0x80;    // enable symbol period from reg
                nim_reg_write(dev, RAD_TSOUT_SYMB + 0x01, &data, 1);

                nim_reg_read(dev, RDC_EQ_DBG_TS_CFG, &data, 1);
                data = data | 0xe0;
                nim_reg_write(dev, RDC_EQ_DBG_TS_CFG, &data, 1);

                data = 0x1d;  //use 98M clk and enable ci+
                nim_reg_write(dev, RDF_TS_OUT_DVBS2, &data, 1);
                nim_reg_read(dev, RD8_TS_OUT_SETTING, &data, 1);
                data = data & 0x08 | 0x07;
                nim_reg_write(dev, RD8_TS_OUT_SETTING, &data, 1);

                if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_2BIT_MODE)
                {
                    //ECO_SSI_2B_EN = cr9f[3]
                    nim_reg_read(dev, R9C_DEMAP_BETA + 0x03, &data, 1);
                    data = data | 0x08;
                    nim_reg_write(dev, R9C_DEMAP_BETA + 0x03, &data, 1);

                    //ECO_SSI_SEL_2B = crc0[3]
                    nim_reg_read(dev, RC0_BIST_LDPC_REG, &data, 1);
                    data = data | 0x08; 
                    nim_reg_write(dev, RC0_BIST_LDPC_REG, &data, 1);
                }
                else if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_4BIT_MODE)
                {
                    //ECO_SSI_2B_EN = cr9f[3]
                    nim_reg_read(dev, R9C_DEMAP_BETA + 0x03, &data, 1);
                    data = data | 0x08;
                    nim_reg_write(dev, R9C_DEMAP_BETA + 0x03, &data, 1);

                    //ECO_SSI_SEL_2B = crc0[3]
                    nim_reg_read(dev, RC0_BIST_LDPC_REG, &data, 1);
                    data = data & 0xf7; 
                    nim_reg_write(dev, RC0_BIST_LDPC_REG, &data, 1);
                }
            }
            //open eco_ts_en
            nim_m3501c_fec_ts_on(dev);
            nim_m3501_ts_on(dev);
        }//end M3501B
    }

    for (FFT_freq = pstAuto_Scan->sfreq; FFT_freq < pstAuto_Scan->efreq; FFT_freq += step_freq)
    {
        nim_s3501_demod_ctrl(dev, NIM_DEMOD_CTRL_0X91);
        nim_s3501_autoscan_signal_input(dev, NIM_SIGNAL_INPUT_OPEN);
        //>>> Unicable begin
        //only unicable need set here!
        if (pstAuto_Scan->unicable)
        {
            pstAuto_Scan->callback(5, 0, FFT_freq, 0, 0);
            if (1 == ret)
            {
                return 1;
            }
            else if (ret == 2)
            {
                goto nim_as_break;
            }
            FFT_result = nim_s3501_ioctl(dev, NIM_DRIVER_FFT, pstAuto_Scan->Fub);
        }
        else
        {  //<<< Unicable end
            FFT_result = nim_s3501_ioctl(dev, NIM_DRIVER_FFT, FFT_freq);
        }
        nim_s3501_autoscan_signal_input(dev, NIM_SIGNAL_INPUT_CLOSE);

        if(dev_priv->ul_status.s3501_autoscan_stop_flag)
        {
            AUTOSCAN_PRINTF("\tleave fuction nim_s3501_autoscan\n");
            return SUCCESS;
        }

        if (SUCCESS == FFT_result)

        {
            step_freq = nim_s3501_ioctl(dev, NIM_FFT_JUMP_STEP, 0);
            crnum = nim_s3501_ioctl(dev, NIM_DRIVER_GET_CR_NUM, 0);
            for (i = 0; i < crnum; i++)
            {
                if (SUCCESS == nim_s3501_channel_search(dev, (UINT32) i))
                {
                    cur_freq = (UINT32) nim_s3501_ioctl(dev, NIM_DRIVER_GET_CUR_FREQ, 0);
                    nim_s3501_get_symbol_rate(dev, &cur_sym);
                    nim_s3501_get_code_rate(dev, &cur_fec);
                    if (i==crnum-1){
                        temp = cur_freq+cur_sym/2000-FFT_freq+45;
                        if (temp<20)
                            temp = 5;
                        else
                            temp = temp - 15;
                        AUTOSCAN_PRINTF("least step_freq is %d, step_freq is %d\n",temp,step_freq);
                        if (temp<step_freq){
                                AUTOSCAN_PRINTF("@@@@@ amy ajust step_freq from %d to %d @@@@@\n",step_freq,temp);
                                step_freq = temp;
                        }
                    }
                    if(NIM_SCAN_SLOW == priv->blscan_mode)
                    {
                        if((priv->ul_status.m_Freq[i]/1024)>last_ch_fc+10)
                        {
                            double_i=0;
                        }
                        if (i==crnum-1)
                        {
                            temp_step = (priv->ul_status.m_Freq[i]/1024) + priv->ul_status.m_Rs[i]/2000 * 1.2 - FFT_freq + 45;
                            temp_step = temp_step - 18;

                            AUTOSCAN_PRINTF("\tleast step_freq is %d, step_freq is %d\n",temp_step,step_freq);
                            if ( temp_step < step_freq )
                            {
                                step_freq = temp_step;
                            }

                            if (step_freq < 5)
                            {
                                step_freq = 5;
                            }
                        }
                    }


                    if ((cur_freq >= pstAuto_Scan->sfreq) && (cur_freq <= pstAuto_Scan->efreq))
                    {
                        //>>> Unicable begin
                        if (pstAuto_Scan->unicable) //cur_freq -> [950, 2150]
                        {
                            AUTOSCAN_PRINTF("\tUnicable FFT_freq: %d, cur_freq: %d, Fub: %d\n", FFT_freq, cur_freq, pstAuto_Scan->Fub);
                            ret = pstAuto_Scan->callback(1, 0, FFT_freq + cur_freq - pstAuto_Scan->Fub, cur_sym, cur_fec);
                        }
                        else    //<<< Unicable end
                        {
                           if((cur_freq>last_tp_freq-3)&&(cur_freq<last_tp_freq+3))
                           {
                               ret = pstAuto_Scan->callback(0, 0, FFT_freq, 0, 0);
                               AUTOSCAN_PRINTF("\tRescan a tp: FFT_freq: %d, last_tp_freq: %d\n", FFT_freq, last_tp_freq);
                           }
                           else
                           {
                                        ret = pstAuto_Scan->callback(1, 0, cur_freq, cur_sym, cur_fec);
                               last_tp_freq=cur_freq;
                           }

                        }
                    }
                    else
                    {
                        if (dev_priv->ul_status.s3501_autoscan_stop_flag)
                                    {
                                        AUTOSCAN_PRINTF("\tleave fuction nim_s3501_autoscan\n");
                                        return SUCCESS;
                        }
                        else
                        {
                            continue ;
                        }
                    }
               }
               else
               {
                    ret = pstAuto_Scan->callback(0, 0, FFT_freq, 0, 0);
                    if (i==crnum-1){
                        if (i>0){
                            temp = (dev_priv->ul_status.m_Freq[i-1]>>10) + dev_priv->ul_status.m_Rs[i-1]/2000 - FFT_freq + 45;
                            if (temp<20)
                                temp = 5;
                            else
                                temp = temp - 15;
                            AUTOSCAN_PRINTF("least step_freq is %d, step_freq is %d\n",temp,step_freq);
                            if (temp<step_freq){
                                if (temp>30){
                                    AUTOSCAN_PRINTF("@@@@@ amy ajust step_freq from %d to %d @@@@@\n",step_freq,temp);
                                    step_freq = temp;
                                }else{
                                    AUTOSCAN_PRINTF("@@@@@ amy ajust step_freq from %d to %d @@@@@\n",step_freq,step_freq-5);
                                    step_freq = step_freq-5;
                                }
                            }
                        }else{
                            AUTOSCAN_PRINTF("least step_freq is 30, step_freq is %d\n",step_freq);
                            if (step_freq>30){
                                AUTOSCAN_PRINTF("@@@@@ amy ajust step_freq from %d to 30 @@@@@\n",step_freq);
                                step_freq = 30;
                            }
                        }
                    }

                    if(NIM_SCAN_SLOW == priv->blscan_mode)            
                    {
                        if((priv->ul_status.m_Freq[i]/1024)>last_ch_fc+10)
                        {
                            double_i=0;
                        }

                        if(double_i==0)
                        {
                            if(priv->ul_status.m_Rs[i]>20000)
                            {
                                step_freq = (priv->ul_status.m_Freq[i]/1024) - priv->ul_status.m_Rs[i]/2000 * 1.2 - FFT_freq + 45-18;
                                last_ch_fc=(priv->ul_status.m_Freq[i]/1024);
                                double_i=1;
                                if(step_freq < 5)
                                    step_freq = 5;
                                AUTOSCAN_PRINTF("\tdouble scan step freq: %d\n",step_freq);
                                break;
                            }
                        }


                        if (i==crnum-1)
                        {
                            if (i>0)
                            {
                                temp_step = (priv->ul_status.m_Freq[i]/1024) - priv->ul_status.m_Rs[i]/2000 *1.2 - FFT_freq + 45;
                                temp_step = temp_step - 18;

                                AUTOSCAN_PRINTF("temp_freq is %d\n",temp_step);
                                if (temp_step<step_freq)
                                {
                                    step_freq = temp_step;
                                    AUTOSCAN_PRINTF("\tamy ajust step_freq from %d to %d @@@@@\n",step_freq,temp_step);
                                }
                                if (step_freq<5)
                                {
                                    step_freq = 5;
                                    AUTOSCAN_PRINTF("\tamy ajust step_freq from %d to 5 @@@@@\n",step_freq);
                                }

                            }
                            else
                            {
                                AUTOSCAN_PRINTF("\tonly 1 channel founed, least step_freq is 24, step_freq is %d\n",step_freq);
                                if (step_freq>24)
                                {
                                    AUTOSCAN_PRINTF("\tamy ajust step_freq from %d to 24 @@@@@\n",step_freq);
                                    step_freq = 24;
                                }

                            }
                        }
                    }
                }
               if (ret == 2)
               {
                   return SUCCESS;
               }
               else if (ret == 1)
               {
                   goto nim_as_break;
               }
            }
            if (crnum == 0)
            {
                if(NIM_SCAN_SLOW == priv->blscan_mode)
                {
                    if(step_freq >= 40)
                    {
                        step_freq = 40;
                    }
                }

                if (pstAuto_Scan->callback(0, 0, FFT_freq, 0, 0) == 1)
                {
                    goto nim_as_break;
                }
            }
        }
        else if(NIM_SCAN_SLOW == priv->blscan_mode)
        {
           step_freq = 5; 
        }
    }

    #ifdef AUTOSCAN_DEBUG
    priv->Tuner_Config_Data.QPSK_Config = config_data;
    #endif

    AUTOSCAN_PRINTF("\tfinish autoscan\n");

    return SUCCESS;

    nim_as_break:


    pstAuto_Scan->callback(2, 0, 0, 0, 0);  /* Tell callback search finished */

    nim_s3501_autoscan_signal_input(dev, NIM_SIGNAL_INPUT_CLOSE);

    return ret;
}
#endif

INT32 nim_s3501_ioctl_ext(struct nim_device *dev, INT32 cmd, void *param_list)
{
    nim_get_fft_result_t *fft_para;
    nim_diseqc_operate_para_t *diseqc_para;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    UINT32 i;
    INT32 result=SUCCESS;
   UINT32 v_scan_loop=0x01;
    NIM_PRINTF("    Enter fuction nim_s3501_event_control\n");

    switch (cmd)
    {
    case NIM_DRIVER_AUTO_SCAN:
        /* Do AutoScan Procedure */
        #ifdef NIM_S3501_ASCAN_TOOLS
        #endif
            #ifdef NIM_S3501_ASCAN_TOOLS
            ascan_stop_flag=0x00;
            #endif
            NIM_PRINTF(">>>[JIE] NIM_DRIVER_AUTO_SCAN\n");
            result=nim_s3501_autoscan(dev, (struct NIM_Auto_Scan *) (param_list));
                #ifdef NIM_S3501_ASCAN_TOOLS
                if(priv->ul_status.s3501_autoscan_stop_flag)
                    ascan_stop_flag=0x01;
                else
                    nim_s3501_ascan_tps_lock_check();
                #endif
                #ifdef NIM_S3501_ASCAN_TOOLS
                //nim_s3501_ascan_end_process();
                #endif
        return result;
    case NIM_DRIVER_CHANNEL_CHANGE:
        /* Do Channel Change */
        NIM_PRINTF(">>>[JIE] NIM_DRIVER_CHANNEL_CHANGE\n");
        {
            struct NIM_Channel_Change *nim_param = (struct NIM_Channel_Change *) (param_list);
            return nim_s3501_channel_change(dev, nim_param->freq, nim_param->sym, nim_param->fec);
        }
    case NIM_DRIVER_CHANNEL_SEARCH:
        /* Do Channel Search */
        break;
    case NIM_DRIVER_GET_ID:
        *((UINT32 *) param_list) = priv->ul_status.m_s3501_type;
        break;
    case NIM_DRIVER_GET_FFT_RESULT:
        fft_para = (nim_get_fft_result_t *)(param_list);
        return nim_s3501_get_fft_result(dev, fft_para->freq, fft_para->start_addr);
        break;
    case NIM_DRIVER_DISEQC_OPERATION:
        diseqc_para = (nim_diseqc_operate_para_t *)(param_list);
        return nim_s3501_DiSEqC_operate(dev, diseqc_para->mode, diseqc_para->cmd, diseqc_para->cnt);
        break;
    case NIM_DRIVER_DISEQC2X_OPERATION:
        diseqc_para = (nim_diseqc_operate_para_t *)(param_list);
        return nim_s3501_DiSEqC2X_operate(dev, diseqc_para->mode, diseqc_para->cmd, diseqc_para->cnt, \
            diseqc_para->rt_value, diseqc_para->rt_cnt);
        break;

        break;
    default:
        break;
    }

    return SUCCESS;
}

INT32 nim_s3501_get_bitmode(struct nim_device *dev, UINT8 *bitMode)
{
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;

    if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_1BIT_MODE)
        *bitMode = 0x60;
    else if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_2BIT_MODE)
        *bitMode = 0x00;
    else if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_4BIT_MODE)
        *bitMode = 0x20;
    else if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_8BIT_MODE)
        *bitMode = 0x40;
    else
        *bitMode = 0x40;
    return SUCCESS;
}


// begin add operation for s3501 optimize

static INT32 nim_s3501_set_err(struct nim_device *dev)
{
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    priv->ul_status.m_Err_Cnts++;
    return SUCCESS;
}
static INT32 nim_s3501_get_err(struct nim_device *dev)
{
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    if (priv->ul_status.m_Err_Cnts > 0x00)
        return ERR_FAILED;
    else
        return SUCCESS;
}
static INT32 nim_s3501_clear_err(struct nim_device *dev)
{
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    priv->ul_status.m_Err_Cnts = 0x00;
    return SUCCESS;
}

static INT32 nim_s3501_get_type(struct nim_device *dev)
{
    UINT8 temp[4] =
    {
        0x00, 0x00, 0x00, 0x00
    };
    UINT32 m_Value = 0x00;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
	
    priv->ul_status.m_s3501_type = 0x00;
    nim_reg_read(dev, RA3_CHIP_ID, temp, 4);
    m_Value = temp[1];
    m_Value = (m_Value << 8) | temp[0];
    m_Value = (m_Value << 8) | temp[3];
    m_Value = (m_Value << 8) | temp[2];
    priv->ul_status.m_s3501_type = m_Value;
    nim_reg_read(dev, RAB_CHIP_SUB_ID, temp, 1);
    if(temp[0] != 0xcc)
        nim_reg_read(dev, RC0_BIST_LDPC_REG, temp, 1);
    priv->ul_status.m_s3501_sub_type = temp[0];
	
#ifdef C3501C_NEW_TSO 
    priv->ul_status.m_tso_mode = 1;
#else
    priv->ul_status.m_tso_mode = 0;
#endif

    return SUCCESS;
}

static INT32 nim_s3501_i2c_open(struct nim_device *dev)
{
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    nim_s3501_clear_err(dev);
    if (priv->Tuner_Config_Data.QPSK_Config & M3501_I2C_THROUGH)
    {
        UINT8 data, ver_data;
        data = 0xd4;
        nim_reg_write(dev, RCB_I2C_CFG, &data, 1);

        nim_reg_read(dev, RB3_PIN_SHARE_CTRL, &ver_data, 1);
        data = ver_data | 0x04;
        nim_reg_write(dev, RB3_PIN_SHARE_CTRL, &data, 1);
        comm_delay(200);

        if (nim_s3501_get_err(dev))
            return ERR_FAILED;
        return SUCCESS;
    }
    return SUCCESS;
}

static INT32 nim_s3501_i2c_close(struct nim_device *dev)
{
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    nim_s3501_clear_err(dev);
    if (priv->Tuner_Config_Data.QPSK_Config & M3501_I2C_THROUGH)
    {
        UINT8 data, ver_data;
        comm_delay(200);
        nim_reg_read(dev, RB3_PIN_SHARE_CTRL, &ver_data, 1);
        data = ver_data & 0xfb;
        nim_reg_write(dev, RB3_PIN_SHARE_CTRL, &data, 1);
        data = 0xc4;
        nim_reg_write(dev, RCB_I2C_CFG, &data, 1);
        NIM_PRINTF("     @@@@Exit nim through\n");

        if (nim_s3501_get_err(dev))
            return ERR_FAILED;
        return SUCCESS;
    }
    return SUCCESS;
}


static INT32 nim_s3501_ext_lnb_config(struct nim_device *dev, struct QPSK_TUNER_CONFIG_API *ptrQPSK_Tuner)
{
    /****For external lnb controller config****/
    struct nim_s3501_private *priv_mem = (struct nim_s3501_private *) dev->priv;
    priv_mem->ext_lnb_control = NULL;
    if (ptrQPSK_Tuner->ext_lnb_config.ext_lnb_control)
    {
        UINT32 check_sum = 0;
        check_sum = (UINT32) (ptrQPSK_Tuner->ext_lnb_config.ext_lnb_control);
        check_sum += ptrQPSK_Tuner->ext_lnb_config.i2c_base_addr;
        check_sum += ptrQPSK_Tuner->ext_lnb_config.i2c_type_id;
        if (check_sum == ptrQPSK_Tuner->ext_lnb_config.param_check_sum)
        {
            priv_mem->ext_lnb_control = ptrQPSK_Tuner->ext_lnb_config.ext_lnb_control;
            priv_mem->ext_lnb_control(0, LNB_CMD_ALLOC_ID, (UINT32) (&priv_mem->ext_lnb_id));
            priv_mem->ext_lnb_control(priv_mem->ext_lnb_id, LNB_CMD_INIT_CHIP, (UINT32) (&ptrQPSK_Tuner->ext_lnb_config));
        }
    }

    return SUCCESS;
}

static INT32 nim_s3501_interrupt_mask_clean(struct nim_device *dev)
{
    UINT8 data = 0x00;
    nim_reg_write(dev, R02_IERR, &data, 1);
    data = 0xff;
    nim_reg_write(dev, R03_IMASK, &data, 1);
    return SUCCESS;
}

static INT32 nim_s3501_sym_config(struct nim_device *dev, UINT32 sym)
{
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    if (sym > 40000)
        priv->ul_status.c_RS = 8;
    else if (sym > 30000)
        priv->ul_status.c_RS = 4;
    else if (sym > 20000)
        priv->ul_status.c_RS = 2;
    else if (sym > 10000)
        priv->ul_status.c_RS = 1;
    else
        priv->ul_status.c_RS = 0;
    return SUCCESS;
}

static INT32 nim_s3501_adc_setting(struct nim_device *dev)
{
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    UINT8 data, ver_data;
    // ADC setting
    if (priv->Tuner_Config_Data.QPSK_Config & M3501_IQ_AD_SWAP)
        data = 0x4a;
    else
        data = 0x0a;

    if (priv->Tuner_Config_Data.QPSK_Config & M3501_EXT_ADC)
        data |= 0x80;
    else
        data &= 0x7f;
    nim_reg_write(dev, R01_ADC, &data, 1);

    nim_reg_read(dev, R01_ADC, &ver_data, 1);
    if (data != ver_data)
    {
        NIM_PRINTF(" wrong 0x8 reg write\n");
        return ERR_FAILED;
    }


    return SUCCESS;
}

static INT32 nim_s3501_set_hw_timeout(struct nim_device *dev, UINT8 time_thr)
{
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;

    // AGC1 setting
    if (time_thr != priv->ul_status.m_hw_timeout_thr)
    {
        nim_reg_write(dev, R05_TIMEOUT_TRH, &time_thr, 1);
        priv->ul_status.m_hw_timeout_thr = time_thr;
    }

    return SUCCESS;
}

static INT32 nim_s3501_agc1_ctrl(struct nim_device *dev, UINT8 low_sym, UINT8 s_Case)
{
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    UINT8 data;

    // AGC1 setting
    if (priv->Tuner_Config_Data.QPSK_Config & M3501_NEW_AGC1)
    {
      switch (s_Case)
      {
        case NIM_OPTR_CHL_CHANGE:
            if (1 == low_sym)
                data = 0xaf;
            else
                data = 0xb5;
            break;
        case NIM_OPTR_SOFT_SEARCH:
            if (1 == low_sym)
                data = 0xb3;
            else
                data = 0xba;
            break;
        case NIM_OPTR_FFT_RESULT:
                data=0xb9;
            break;
        }
    }
    else
    {
      switch (s_Case)
      {
        case NIM_OPTR_CHL_CHANGE:
        case NIM_OPTR_SOFT_SEARCH:
            if (1 == low_sym)
                data = 0x3c;
            else
                data = 0x54;
            break;
        case NIM_OPTR_FFT_RESULT:
            data = 0x54;
            break;
      }
    }
    if ((priv->Tuner_Config_Data.QPSK_Config & M3501_AGC_INVERT) == 0x0)  
        data = data ^ 0x80;

    nim_reg_write(dev, R07_AGC1_CTRL, &data, 1);

    data = 0x58;
    nim_reg_write(dev, R07_AGC1_CTRL+0x01, &data, 1);
    data = 0x50; 
    nim_reg_write(dev, R0A_AGC1_LCK_CMD, &data, 1);	

    return SUCCESS;
}

static INT32 nim_s3501_freq_offset_set(struct nim_device *dev, UINT8 low_sym, UINT32 *s_Freq)
{
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;

    if (priv->Tuner_Config_Data.QPSK_Config & M3501_QPSK_FREQ_OFFSET)
    {
        if (1 == low_sym)
            *s_Freq += 3;
    }
    return SUCCESS;
}

static INT32 nim_s3501_freq_offset_reset(struct nim_device *dev, UINT8 low_sym)
{
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;

    if (priv->Tuner_Config_Data.QPSK_Config & M3501_QPSK_FREQ_OFFSET)
    {
        if (1 == low_sym)
            nim_s3501_set_freq_offset(dev, -3000);
        else
            nim_s3501_set_freq_offset(dev, 0);
    }
    else
        nim_s3501_set_freq_offset(dev, 0);

    return SUCCESS;
}

static INT32 nim_s3501_freq_offset_reset1(struct nim_device *dev, UINT8 low_sym, INT32 delfreq)
{
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;

    if (priv->Tuner_Config_Data.QPSK_Config & M3501_QPSK_FREQ_OFFSET)
    {
        if (1 == low_sym)
            nim_s3501_set_freq_offset(dev, -3000 + delfreq);
        else
            nim_s3501_set_freq_offset(dev, delfreq);
    }
    else
        nim_s3501_set_freq_offset(dev, delfreq);

    return SUCCESS;
}

static INT32 nim_s3501_cr_setting(struct nim_device *dev, UINT8 s_Case)
{
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    UINT8 data;
    switch (s_Case)
    {
    case NIM_OPTR_SOFT_SEARCH:
        // set CR parameter
        data = 0xaa;
        nim_reg_write(dev, R33_CR_CTRL + 0x03, &data, 1);
        data = 0x45;
        nim_reg_write(dev, R33_CR_CTRL + 0x04, &data, 1);
        data = 0x97;
        nim_reg_write(dev, R33_CR_CTRL + 0x05, &data, 1);

        if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
            data = 0xab;
        else
            data = 0xaa; // S2 CR parameter

        nim_reg_write(dev, RB5_CR_PRS_TRA, &data, 1);
        break;
    case NIM_OPTR_CHL_CHANGE:
        // set CR parameter
        data = 0xaa;
        nim_reg_write(dev, R33_CR_CTRL + 0x03, &data, 1);
        data = 0x45;
        nim_reg_write(dev, R33_CR_CTRL + 0x04, &data, 1);
        data = 0x87;
        nim_reg_write(dev, R33_CR_CTRL + 0x05, &data, 1);

        if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
            data = 0xaa;
        else
            data = 0xaa; // S2 CR parameter

        nim_reg_write(dev, RB5_CR_PRS_TRA, &data, 1);

        break;
    }

    return SUCCESS;
}

static INT32 nim_s3501_ldpc_setting(struct nim_device *dev, UINT8 s_Case, UINT8 c_ldpc, UINT8 c_fec)
{
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    UINT8 data;
    // LDPC parameter
    switch (s_Case)
    {
    case NIM_OPTR_CHL_CHANGE:
        if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
        {
            UINT8 temp[3] =
            {
                0x32, 0xc8, 0x08
            };
            nim_reg_write(dev, R57_LDPC_CTRL, temp, 3);
        }
        else
        {
            UINT8 temp[3] =
            {
                0x32, 0x44, 0x04
            };
            temp[0] = 0x1e - priv->ul_status.c_RS;    
            nim_reg_write(dev, R57_LDPC_CTRL, temp, 3);
        }
        data = 0x01;  
        nim_reg_write(dev, RC1_DVBS2_FEC_LDPC, &data, 1);


        break;
    case NIM_OPTR_SOFT_SEARCH:
        if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
        {
            UINT8 temp[3] =
            {
                0x32, 0x48, 0x08
            };
            nim_reg_write(dev, R57_LDPC_CTRL, temp, 3);
        }
        else
        {
            UINT8 temp[3] =
            {
                0x1e, 0x46, 0x06
            };
            nim_reg_write(dev, R57_LDPC_CTRL, temp, 3);
        }
        data = 0x01;  
        nim_reg_write(dev, RC1_DVBS2_FEC_LDPC, &data, 1);

        break;
    case NIM_OPTR_DYNAMIC_POW:
        data = c_ldpc - priv->ul_status.c_RS;
        nim_reg_write(dev, R57_LDPC_CTRL, &data, 1);

        break;
    }
    data = c_fec;
    nim_reg_write(dev, RC1_DVBS2_FEC_LDPC, &data, 1);

    return SUCCESS;
}

static INT32 nim_s3501_hw_init(struct nim_device *dev)
{
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    UINT8 data = 0xc0;
    nim_reg_write(dev, RA7_I2C_ENHANCE, &data, 1);

    nim_reg_read(dev, RCC_STRAP_PIN_CLOCK, &data, 1);
    data = data & 0xfa;    
    data = data | 0x20;      
    nim_reg_write(dev, RCC_STRAP_PIN_CLOCK, &data, 1);

    data = 0x10;
    nim_reg_write(dev, RB3_PIN_SHARE_CTRL, &data, 1);
    data = 0x1f; 
    nim_reg_write(dev, R1B_TR_TIMEOUT_BAND, &data, 1);

    data = 0x84;
    nim_reg_write(dev, R28_PL_TIMEOUT_BND + 0x01, &data, 1);

    // Set Hardware time out
    nim_s3501_set_hw_timeout(dev, 0xff);

    //----eq demod setting
    data = 0x04;  
    nim_reg_write(dev, R21_BEQ_CTRL, &data, 1);

    data = 0x24;       
    nim_reg_write(dev, R25_BEQ_MASK, &data, 1);

    if ((priv->Tuner_Config_Data.QPSK_Config & 0xc0) == M3501_1BIT_MODE)
        data = 0x08;
    else
        data = 0x00;
    nim_reg_write(dev, RAF_TSOUT_PAD, &data, 1);

    data = 0x00;
    nim_reg_write(dev, RB1_TSOUT_SMT, &data, 1);

    data = 0x6c;
    nim_reg_write(dev, R2A_PL_BND_CTRL + 0x02, &data, 1);
    //----eq demod setting end

    nim_s3501_adc_setting(dev);

    #ifdef SW_ADPT_CR
    if(priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B && priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C)
    {
        data = 0xc3;
        nim_reg_write(dev, R0E_ADPT_CR_CTRL, &data, 1);
        data = 0x9e;
        nim_reg_write(dev, R0E_ADPT_CR_CTRL, &data, 1);
        nim_reg_read(dev, RE0_PPLL_CTRL, &data, 1);
        data |= 0x08;
        nim_reg_write(dev, RE0_PPLL_CTRL, &data, 1); 
    }
    #endif

    #ifdef HW_ADPT_CR
    if(priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B && priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C)
    {
        nim_cr_tab_init(dev);
        data = 0x81;
        nim_reg_write(dev, R0E_ADPT_CR_CTRL, &data, 1);

        #ifdef HW_ADPT_CR_MONITOR
            data = 0xe6; 
            nim_reg_write(dev, R0E_ADPT_CR_CTRL, &data, 1);
        #endif
    }
    #endif


    // config EQ
#ifdef DFE_EQ
    data = 0xf0;
    nim_reg_write(dev, RD7_EQ_REG, &data, 1);
    nim_reg_read(dev, RDC_EQ_DBG_TS_CFG, &data, 1);
    data = data | 0x81;
    nim_reg_write(dev, RDC_EQ_DBG_TS_CFG, &data, 1);
#else
    data = 0x00;
    nim_reg_write(dev, RD7_EQ_REG, &data, 1);
    nim_reg_read(dev, RDC_EQ_DBG_TS_CFG, &data, 1);
    data = data | 0x81;
    nim_reg_write(dev, RDC_EQ_DBG_TS_CFG, &data, 1);
#endif

    return SUCCESS;
}

static INT32 nim_s3501_demod_ctrl(struct nim_device *dev, UINT8 c_Value)
{
    UINT8 data = c_Value;
    nim_reg_write(dev, R00_CTRL, &data, 1);
    return SUCCESS;
}

static INT32 nim_s3501_hbcd_timeout(struct nim_device *dev, UINT8 s_Case)
{
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    UINT8 data;
    switch (s_Case)
    {
    case NIM_OPTR_CHL_CHANGE:
    case NIM_OPTR_IOCTL:
        if (priv->t_Param.t_reg_setting_switch & NIM_SWITCH_HBCD)
        {
            if (priv->ul_status.m_enable_dvbs2_hbcd_mode)
                data = priv->ul_status.m_dvbs2_hbcd_enable_value;
            else
                data = 0x00;
            nim_reg_write(dev, R47_HBCD_TIMEOUT, &data, 1);
            priv->t_Param.t_reg_setting_switch &= ~NIM_SWITCH_HBCD;
        }
        break;
    case NIM_OPTR_SOFT_SEARCH:
        if (!(priv->t_Param.t_reg_setting_switch & NIM_SWITCH_HBCD))
        {
            data = 0x00;
            nim_reg_write(dev, R47_HBCD_TIMEOUT, &data, 1);
            priv->t_Param.t_reg_setting_switch |= NIM_SWITCH_HBCD;
        }
        break;
    case NIM_OPTR_HW_OPEN:
        nim_reg_read(dev, R47_HBCD_TIMEOUT, &(priv->ul_status.m_dvbs2_hbcd_enable_value), 1);
        break;
    }
    return SUCCESS;
}

static INT32 nim_s3501_set_acq_workmode(struct nim_device *dev, UINT8 s_Case)
{
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    UINT8 data, work_mode = 0x00;
    switch (s_Case)
    {
    case NIM_OPTR_CHL_CHANGE0:
    case NIM_OPTR_DYNAMIC_POW0:
        if(CRYSTAL_FREQ > 15000)
            data = 0xf3;
        else
            data = 0x73;
        nim_reg_write(dev, R5B_ACQ_WORK_MODE, &data, 1);
        break;
    case NIM_OPTR_CHL_CHANGE:
        nim_s3501_reg_get_work_mode(dev, &work_mode);
        if (work_mode != M3501_DVBS2_MODE)
        {
            if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
                priv->ul_status.phase_err_check_status = 1000;
            if(CRYSTAL_FREQ > 15000)
                data = 0xf7;
            else
                data = 0x77;
            nim_reg_write(dev, R5B_ACQ_WORK_MODE, &data, 1);
        }
        break;
    case NIM_OPTR_SOFT_SEARCH:
    case NIM_OPTR_DYNAMIC_POW:
        if(CRYSTAL_FREQ > 15000)
            data = 0xf7;
        else
            data = 0x77;
        nim_reg_write(dev, R5B_ACQ_WORK_MODE, &data, 1);
        break;
    case NIM_OPTR_HW_OPEN:
        if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
        {
            // enable ADC
            data = 0x00;
            nim_reg_write(dev, RA0_RXADC_REG + 0x02, &data, 1);
            // enable S2 clock
            if(CRYSTAL_FREQ > 15000)
                data = 0xf3;
            else
                data = 0x73;
            nim_reg_write(dev, R5B_ACQ_WORK_MODE, &data, 1);
        }
        break;
    case NIM_OPTR_HW_CLOSE:
        if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
        {
            // close ADC
            data = 0x07;
            nim_reg_write(dev, RA0_RXADC_REG + 0x02, &data, 1);
            // close S2 clock
            data = 0x3f;
        }
        else
        {
            data = 0x7f;
        }
        nim_reg_write(dev, R5B_ACQ_WORK_MODE, &data, 1);
        break;
    }
    return SUCCESS;
}

static INT32 nim_s3501_set_FC_Search_Range(struct nim_device *dev, UINT8 s_Case, UINT32 rs)
{
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    UINT8 data, ver_data;
    UINT32 sample_rate;
    UINT32 temp;

    nim_s3501_get_dsp_clk (dev, &sample_rate);
    sample_rate=((sample_rate+500)/1000);
    switch (s_Case)
    {
    case NIM_OPTR_SOFT_SEARCH:
        {
            if (rs > 16000)
                temp = 5 * 16;
            else if (rs > 5000)
                temp = 4 * 16;
            else
                temp = 3 * 16;
            data = temp & 0xff;
            nim_reg_write(dev, R62_FC_SEARCH, &data, 1);
            data = (temp >> 8) & 0x3;
            if (rs > 10000)
                data |= 0xa0;      
            else if (rs > 3000)
                data |= 0xc0;      
            else
                data |= 0xb0;      
            nim_reg_write(dev, R62_FC_SEARCH + 0x01, &data, 1);
            priv->t_Param.t_reg_setting_switch |= NIM_SWITCH_FC;
        }
        break;
    case NIM_OPTR_CHL_CHANGE:
        //if (priv->t_Param.t_reg_setting_switch & NIM_SWITCH_FC)
        {
            //temp = ((3*90*16) + (sample_rate/2)) / sample_rate;
            if (rs > 18000)
                temp = 5 * 16;
            else if (rs > 15000)
                temp = 4 * 16;
            else if (rs > 4000)
                temp = 3 * 16;
            else
                temp = 2 * 16;
            
            data = temp & 0xff;
            nim_reg_write(dev, R62_FC_SEARCH, &data, 1);

            data = (temp >> 8) & 0x3;
            data |= 0xb0;
            nim_reg_write(dev, R62_FC_SEARCH + 0x01, &data, 1);
            priv->t_Param.t_reg_setting_switch &= ~NIM_SWITCH_FC;
        }

        break;
    }
    return SUCCESS;
}

static INT32 nim_s3501_RS_Search_Range(struct nim_device *dev, UINT8 s_Case, UINT32 rs)
{
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    UINT8 data, ver_data;
    UINT32 sample_rate;
    UINT32 temp;
	
    nim_s3501_get_dsp_clk (dev, &sample_rate);
    sample_rate=((sample_rate+500)/1000);
    switch (s_Case)
    {
    case NIM_OPTR_SOFT_SEARCH:
        {
            //CR64
            temp = rs / 2000;

            if (temp < 3)
                temp = 3;
            else if (temp > 11)
                temp = 11;
            temp = temp << 4;
            data = temp & 0xff;
            nim_reg_write(dev, R64_RS_SEARCH, &data, 1);
            data = (temp >> 8) & 0x3;
            data |= 0x90;     
            nim_reg_write(dev, R64_RS_SEARCH + 0x01, &data, 1);
            priv->t_Param.t_reg_setting_switch |= NIM_SWITCH_RS;
        }

        break;
    case NIM_OPTR_CHL_CHANGE:
        if (priv->t_Param.t_reg_setting_switch & NIM_SWITCH_RS)
        {
            temp = ((3*90*16) + sample_rate/2) / sample_rate;
            data = temp & 0xff;
            nim_reg_write(dev, R64_RS_SEARCH, &data, 1);

            data = (temp >> 8) & 0x3;
            data |= 0x30;
            nim_reg_write(dev, R64_RS_SEARCH + 0x01, &data, 1);
            priv->t_Param.t_reg_setting_switch &= ~NIM_SWITCH_RS;
        }

        break;
    }
    return SUCCESS;
}

static INT32 nim_s3501_cap_IQ_enerage(struct nim_device *dev)
{
    UINT8 data = 0;
    UINT8 buffer[8];
    INT32 i = 0, j = 0, k = 0;
    UINT8 fft_data[3];
    INT32 fft_I, fft_Q;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;

    for (i = 0; i < 1024; i++)
    {
        data = (UINT8) ((i) & 0xff);
        buffer[0] = data;
        data = (UINT8) (i >> 8);
        data &= 0x3;
        buffer[1] = data;
        nim_reg_write(dev, R70_CAP_REG, buffer, 2);
        nim_reg_read(dev, R70_CAP_REG + 0x02, fft_data, 2);

        fft_I = fft_data[0];
        fft_Q = fft_data[1];

        if (fft_I & 0x80)
            fft_I |= 0xffffff00;
        if (fft_Q & 0x80)
            fft_Q |= 0xffffff00;

        k = 0;
        for (j = 0; j < 10; j++)
        {
            k <<= 1;k += ((i >> j) & 0x1);
        }

        fft_I = fft_I << (FFT_BITWIDTH - 8);
        fft_Q = fft_Q << (FFT_BITWIDTH - 8);

        priv->ul_status.FFT_I_1024[k] = fft_I;
        priv->ul_status.FFT_Q_1024[k] = fft_Q;
    }
if(1==M3501_Debug_Flag&(0x01))
{
for(i = 0; i< 1024; i++)
{
	AUTOSCAN_PRINTF("ADCdata%d:[%d,%d]\n",i,priv->ul_status.FFT_I_1024[i] ,priv->ul_status.FFT_Q_1024[i]);
}
}

    return SUCCESS;
}

static INT32 nim_s3501_TR_CR_Setting(struct nim_device *dev, UINT8 s_Case)
{
    UINT8 data = 0;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;
    switch (s_Case)
    {
    case NIM_OPTR_SOFT_SEARCH:
        {
            data = 0x49;
            nim_reg_write(dev, R66_TR_SEARCH, &data, 1);
            data = 0x31;
            nim_reg_write(dev, R67_VB_CR_RETRY, &data, 1);
        }
        break;
    case NIM_OPTR_CHL_CHANGE:
        if (priv->t_Param.t_reg_setting_switch & NIM_SWITCH_TR_CR)
        {
            data = 0x59;
            nim_reg_write(dev, R66_TR_SEARCH, &data, 1);
            data = 0x33;
            nim_reg_write(dev, R67_VB_CR_RETRY, &data, 1);
            priv->t_Param.t_reg_setting_switch &= ~NIM_SWITCH_TR_CR;
        }
        break;
    }
    return SUCCESS;
}
static INT32 nim_s3501_task_init(struct nim_device *dev)
{
    UINT8 nim_device[3][3] =
    {
        'N', 'M', '0', 'N', 'M', '1', 'N', 'M', '2'
    };
    T_CTSK nim_task_praram;
    static UINT8 nim_task_num = 0x00;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;

    if (nim_task_num > 1)
        return SUCCESS;

    nim_task_praram.task = nim_s3501_task;
    nim_task_praram.name[0] = nim_device[nim_task_num][0];
    nim_task_praram.name[1] = nim_device[nim_task_num][1];
    nim_task_praram.name[2] = nim_device[nim_task_num][2];
    nim_task_praram.stksz = 0xc00 ;
    nim_task_praram.itskpri = OSAL_PRI_NORMAL;
    nim_task_praram.quantum = 10 ;
    nim_task_praram.para1 = (UINT32) dev ;
    nim_task_praram.para2 = 0 ;
    priv->tsk_status.m_task_id = osal_task_create(&nim_task_praram);
    if (OSAL_INVALID_ID == priv->tsk_status.m_task_id)
    {
        return OSAL_E_FAIL;
    }
    nim_task_num++;

    return SUCCESS;
}

static void nim_s3501_task(UINT32 param1, UINT32 param2)
{
    struct nim_device *dev = (struct nim_device *) param1 ;
    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv ;
    priv->tsk_status.m_sym_rate = 0x00;
    priv->tsk_status.m_code_rate = 0x00;
    priv->tsk_status.m_map_type = 0x00;
    priv->tsk_status.m_work_mode = 0x00;
    priv->tsk_status.m_info_data = 0x00;
    NIM_PRINTF("Enter nim_s3501_task:\n");
    UINT32 v_cnt_val=0x00;

#ifdef CHANNEL_CHANGE_ASYNC
    UINT32 flag_ptn;
#endif

    while (1)
    {
#ifdef CHANNEL_CHANGE_ASYNC
        flag_ptn = 0;
        if (NIM_FLAG_WAIT(&flag_ptn, priv->flag_id, NIM_FLAG_CHN_CHG_START, OSAL_TWF_ANDW | OSAL_TWF_CLR, 0) == OSAL_E_OK)
        {
            NIM_FLAG_SET(priv->flag_id, NIM_FLAG_CHN_CHANGING);
            osal_mutex_lock(priv->m3501_mutex,OSAL_WAIT_FOREVER_TIME);
            nim_s3501_waiting_channel_lock(dev, priv->cur_freq, priv->cur_sym);
            osal_mutex_unlock(priv->m3501_mutex);
            NIM_FLAG_CLEAR(priv->flag_id, NIM_FLAG_CHN_CHANGING);
            NIM_PRINTF("\t\t Here is the task for M3501C wait channel lock \n");
        }
#endif
        if (priv->ul_status.m_s3501_type == NIM_CHIP_ID_M3501B)
        {
            if((priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C) && (priv->tsk_status.m_lock_flag == NIM_LOCK_STUS_CLEAR))
            {
                if(v_cnt_val  == 10)
                {
                    if(priv->ul_status.m_tso_mode == 1)
                        nim_m3501c_get_int(dev);
                    v_cnt_val = 0;
                    NIM_PRINTF("\t\t Here is the task for M3501C plug_in_out \n");
                    #ifdef HW_ADPT_CR
                        #ifdef HW_ADPT_CR_MONITOR
                            nim_hw_adaptive_cr_monitor(dev);
                        #endif
                    #else
                        #ifdef SW_ADPT_CR
                            #ifdef SW_SNR_RPT_ONLY
                                nim_sw_snr_rpt(dev);
                            #else
                                nim_sw_adaptive_cr(dev);
                            #endif
                        #endif
                    #endif

                }
                else
                    v_cnt_val ++;
            }
            else
            {
                if ((priv->tsk_status.m_lock_flag == NIM_LOCK_STUS_SETTING) && (priv->t_Param.t_i2c_err_flag == 0x00))
                {
                    nim_s3501_get_lock(dev, &(priv->tsk_status.m_info_data));
                    if (priv->tsk_status.m_info_data && (priv->t_Param.t_i2c_err_flag == 0x00))
                    {
                        nim_s3501_reg_get_symbol_rate(dev, &(priv->tsk_status.m_sym_rate));
                        nim_s3501_reg_get_code_rate(dev, &(priv->tsk_status.m_code_rate));
                        nim_s3501_reg_get_work_mode(dev, &(priv->tsk_status.m_work_mode));
                        nim_s3501_reg_get_map_type(dev, &(priv->tsk_status.m_map_type));
                        if ((priv->ul_status.m_enable_dvbs2_hbcd_mode == 0) &&
                            ((priv->tsk_status.m_map_type == 0) || (priv->tsk_status.m_map_type == 5)))
                        {
                            NIM_PRINTF("            Demod Error: wrong map_type is %d\n", priv->tsk_status.m_map_type);
                        }
                        else
                        {
                            if((priv->ul_status.m_s3501_sub_type == NIM_CHIP_SUB_ID_M3501C) && (priv->ul_status.m_tso_mode == 1))
                            {
                                if((priv->tsk_status.m_work_mode) == M3501_DVBS2_MODE)
                                {
                                    nim_m3501c_open_dummy(dev);
                                }
                                else
                                {
                                    nim_m3501c_close_dummy(dev);
                                }
#ifdef C3501C_ERRJ_LOCK
                                nim_m3501c_recover_moerrj(dev);
#endif
                                nim_s3501_clear_int(dev);
                            }
                            else
                            {
                                nim_s3501_set_ts_mode(dev, priv->tsk_status.m_work_mode, priv->tsk_status.m_map_type, priv->tsk_status.m_code_rate,
                                    priv->tsk_status.m_sym_rate, 0x1);
                                // open TS
                                nim_m3501c_fec_ts_on(dev);
                                nim_m3501_ts_on(dev);
                                priv->tsk_status.m_info_data = priv->tsk_status.m_info_data | 0x80;    // ts open
                                nim_s3501_clear_int(dev);
                            }
                            priv->tsk_status.m_lock_flag = NIM_LOCK_STUS_CLEAR;
                        }
                    }
                }
            }
        }
        else
        {
            break;
        }
        comm_sleep(100);
    }
}


static INT32 nim_sw_snr_rpt(struct nim_device *dev)
{
    UINT8 data;
    static UINT16 snr_max = 0;
    static UINT16 snr_min = 0;
    UINT16 var;
    static INT interval_cnt = 0;

    interval_cnt++;
    if(interval_cnt==10)
    {
        nim_reg_read(dev, RBC_SNR_RPT2, &data, 1);
        if (data & 0x80) 
        {
            var = (data & 0x0f) << 6;
            nim_reg_read(dev, RBB_SNR_RPT1, &data, 1); 
            var += (data>>2) & 0x03f;

            if(snr_initial_en)
            {
                snr_max = var;
                snr_min = var;
                snr_initial_en = 0;
            }

            if(var>snr_max)
                snr_max = var;
            if(var<snr_min)
                snr_min = var;

            data = 0x00;
            nim_reg_write(dev, RBC_SNR_RPT2, &data, 1); 

            NIM_PRINTF("*** SNR = %u min=%u max=%u\n", var, snr_min, snr_max);
        }

        interval_cnt = 0;
    }

    return SUCCESS;
}

static INT32 nim_sw_adaptive_cr(struct nim_device *dev)
{
    UINT8 data;

    static UINT16 high_thr = 0;
    static UINT16 low_thr = 0;
    static int curr_snr_idx = 0;
    static UINT16 snr_lpf = 0;
    UINT16 var;
    INT snr_chg=0;
    INT i;
    static INT interval_cnt = 0;

    UINT16 cr_data;
    UINT8 frac,prs,irs;
    UINT16 *CR_PARA;

    nim_reg_read(dev, RBC_SNR_RPT2, &data, 1);
    if (data & 0x80) 
    {
        var = (data & 0x0f) << 6;
        nim_reg_read(dev, RBB_SNR_RPT1, &data, 1); 
        var += (data>>2) & 0x03f;

        if(snr_initial_en)
            snr_lpf = var;
        else
        {
            snr_lpf = (snr_lpf*7+var)>>2;
            snr_lpf += 1;
            snr_lpf >>= 1;
        }
        interval_cnt++;
        if(interval_cnt==16)
        {
            interval_cnt = 0;
        }

        if(snr_initial_en) 
        {
            curr_snr_idx = TAB_SIZE-1;
            for(i=TAB_SIZE-1; i>=0; i--)
            {
                if(var>=SNR_THR[i*2])
                    curr_snr_idx = i;
                else
                    break;
            }

            snr_initial_en = 0;
            snr_chg = 1;
            snr_lpf = var;
        }
        else if(curr_snr_idx>0 && snr_lpf>=SNR_THR[2*(curr_snr_idx-1)] && (interval_cnt==0)) 
        {
            curr_snr_idx--;
            snr_chg = 1;
        }
        else if(curr_snr_idx<TAB_SIZE-1 && snr_lpf<=SNR_THR[2*curr_snr_idx+3] && (interval_cnt==0)) 
        {
            curr_snr_idx++;
            snr_chg = 1;
        }

        if(snr_chg)
        {
            CR_PARA = (UINT16 *)CR_PARA_8PSK_3f5;

            cr_data = CR_PARA[curr_snr_idx];
            frac = cr_data & 0x0f;
            prs = (cr_data>>4) & 0x0f;
            irs = (cr_data>>8) & 0x1f;
            data = (((irs & 0x03)<<4) | frac)<<2;
            nim_reg_write(dev, RBB_SNR_RPT1, &data, 1);

            data = ((irs>>2)<<4) | prs | 0x80;
            nim_reg_write(dev, RBC_SNR_RPT2, &data, 1);

            data = 0xe8;
            nim_reg_write(dev, R0E_ADPT_CR_CTRL, &data, 1);


            data = (curr_snr_idx&0x01) ? 5:0;
            NIM_PRINTF("--->switch to %d.%d dB Setting \n", 6+curr_snr_idx/2,data);
        }

        data = 0x00;
        nim_reg_write(dev, RBC_SNR_RPT2, &data, 1);

    }

    return SUCCESS;
}

static INT32 nim_cr_tab_init(struct nim_device *dev)
{
    UINT8 TabID,CellID;
    UINT16 TabVal, TabValTemp;
    UINT8 data;
    UINT8 DatArray[2];
    const UINT16 OTHERS_TAB[4] = {0x29,     0x00,      0x002,   0x01F8};


    #ifdef HW_ADPT_CR_MONITOR
        NIM_PRINTF("CR TAB Initialization Begin \n");
    #endif
    data = 0xe1;
    nim_reg_write(dev, R0E_ADPT_CR_CTRL, &data, 1);

    for(CellID=0; CellID<TAB_SIZE; CellID++)
    {
        nim_reg_write(dev, R9D_RPT_DEMAP_BETA, &CellID, 1);
        TabVal = SNR_THR[2*CellID] & 0x0fff;
        DatArray[0] = (UINT8)(TabVal & 0x0ff);
        DatArray[1] = (UINT8)(((TabVal>>8) & 0x0ff) | 0x10);
        nim_reg_write(dev, R11_DCC_OF_I, DatArray, 2);
    }

    for(CellID=0; CellID<TAB_SIZE; CellID++)
    {
        data = 0x10 | CellID;
        nim_reg_write(dev, R9D_RPT_DEMAP_BETA, &data, 1);

        TabVal = SNR_THR[2*CellID+1] & 0x0fff;
        DatArray[0] = (UINT8)(TabVal & 0x0ff);
        DatArray[1] = (UINT8)(((TabVal>>8) & 0x0ff) | 0x10);
        nim_reg_write(dev, R11_DCC_OF_I, DatArray, 2);
    }


    const UINT16 *pCrPara;
    for(TabID=2; TabID<=7; TabID++)
    {
        if(TabID==2)
            pCrPara = CR_PARA_8PSK_3f5;
        else
            pCrPara = CR_PARA_8PSK_others;

        for(CellID=0; CellID<TAB_SIZE; CellID++)
        {
            data = (TabID<<4) | CellID;
            nim_reg_write(dev, R9D_RPT_DEMAP_BETA, &data, 1);

            TabVal = pCrPara[CellID] & 0x1fff;
            TabValTemp = (TabVal & 3)<<9;
            TabVal = (TabVal>>4) | TabValTemp;
            DatArray[0] = (UINT8)(TabVal & 0x0ff);
            DatArray[1] = (UINT8)(((TabVal>>8) & 0x0ff) | 0x10);
            nim_reg_write(dev, R11_DCC_OF_I, DatArray, 2);
        }
    }


    // ON/OFF Table
    for(CellID=0; CellID<4; CellID++)
    {
        data = 0xf0 | CellID;
        nim_reg_write(dev, R9D_RPT_DEMAP_BETA, &data, 1);
        TabVal = OTHERS_TAB[CellID];
        DatArray[0] = (UINT8)(TabVal & 0x0ff);
        DatArray[1] = (UINT8)(((TabVal>>8) & 0x0ff) | 0x10);
        nim_reg_write(dev, R11_DCC_OF_I, DatArray, 2);
    }

    data = 0xe0; 
    nim_reg_write(dev, R0E_ADPT_CR_CTRL, &data, 1);

    #ifdef HW_ADPT_CR_MONITOR
        NIM_PRINTF("CR TAB Initialization Done \n");
    #endif

}

static void nim_hw_adaptive_cr_monitor(struct nim_device *dev)
{

    static UINT8 old_cr_para[2]={0,0};
    static UINT8 interval_cnt = 0;
    UINT8 data[2], snr_idx, cr_prs, cr_irs, cr_frac;

    if(interval_cnt==0)
    {
        nim_reg_read(dev, RBB_SNR_RPT1, data, 2);
        if(data[0]!=old_cr_para[0] || data[1]!=old_cr_para[1])
        {
            old_cr_para[0] = data[0];
            old_cr_para[1] = data[1];

            cr_frac = (old_cr_para[0]>>2) & 0x03;
            cr_irs = (old_cr_para[0]>>4) & 0x0f;
            if(cr_irs<=6)
                cr_irs += 16;

            cr_prs = old_cr_para[1] & 0x07;
            if(cr_prs<4)
                cr_prs += 8;

            snr_idx = (old_cr_para[1]>>3) & 0x1f;

            data[0] = (snr_idx & 0x01) ? 5:0;
            NIM_PRINTF("--->switch to %d.%d dB Setting \n", 6+snr_idx/2,data[0]);

            NIM_PRINTF("OldCR:%d Wider:%d PRS:%1x IRS:%x FRAC:%d \n",
                               old_cr_para[0] & 0x01, (old_cr_para[0]>>1) & 0x01, cr_prs, cr_irs,cr_frac);

        }

        interval_cnt=16;
    }
    interval_cnt--;
};

static INT32 nim_s3501_get_new_BER(struct nim_device *dev, UINT32 *ber)
{
    UINT8 data;
    UINT32 t_count, myber;

    myber = 0;
    for (t_count = 0; t_count < 200; t_count++)
    {
        nim_reg_read(dev, R76_BIT_ERR + 0x02, &data, 1);
        if ((data & 0x80) == 0)
        {
            myber = data & 0x7f;
            nim_reg_read(dev, R76_BIT_ERR + 0x01, &data, 1);
            myber <<= 8;
            myber += data;
            nim_reg_read(dev, R76_BIT_ERR, &data, 1);
            myber <<= 8;
            myber += data;
            break;
        }
    }
    *ber = myber;

    return SUCCESS;
}

static INT32 nim_s3501_get_new_PER(struct nim_device *dev, UINT32 *per)
{
    UINT8 data;
    UINT32 t_count, myper;

    myper = 0;
    for (t_count = 0; t_count < 200; t_count++)
    {
        nim_reg_read(dev, R79_PKT_ERR + 0x01, &data, 1);
        if ((data & 0x80) == 0)
        {
            myper = data & 0x7f;
            nim_reg_read(dev, R79_PKT_ERR, &data, 1);
            myper <<= 8;
            myper += data;
            break;
        }
    }
    *per = myper;
    return SUCCESS;
}
INT32 nim_s3501_reg_get_freqoffset(struct nim_device *dev)
{
    INT32 freq_off;
    UINT8 data[3];
    UINT32 tdata;
    UINT32 temp;
    UINT32  sample_rate;


    struct nim_s3501_private *priv = (struct nim_s3501_private *) dev->priv;

    nim_s3501_get_dsp_clk(dev, &sample_rate);
    temp = 0;
    nim_reg_read(dev, R69_RPT_CARRIER, data, 3);

    tdata = (data[0] & 0xff) + ((data[1] & 0xff)<<8);

    if ((data[2] & 0x01) == 1)
        temp = (tdata ^ 0xffff) + 1;
    else
        temp = tdata & 0xffff;

    if ((data[2] & 0x01) == 1)
        freq_off = 0 - nim_s3501_multu64div(temp, sample_rate, 92160000); // 92160000 == 90000*1024
    else
        freq_off = nim_s3501_multu64div(temp, sample_rate, 92160000);
    return freq_off;
}

#ifdef NIM_S3501_ASCAN_TOOLS
static INT32 nim_s3501_ascan_init()
{
    return SUCCESS;

}
static INT32 nim_s3501_ascan_print()
{
    UINT32 i;
    if(!vs_s3501_ascan.va_ascan_g4_open_log_print)
        return SUCCESS;
    ASCAN_PRINTF("\nLoop:%d, TPs:%d,    Autoscan Time:%d\n",vs_s3501_ascan.va_ascan_g8_loop_cur_idx+1,va_g3_ascan_real_tps_index,vs_s3501_ascan.va_ascan_g1_end_time-vs_s3501_ascan.va_ascan_g1_real_begin_time-lck_time_cal);
    ASCAN_PRINTF("Got TPs:\n");
    for(i=0;i<VD_TP_CNT;i++)
    {
        if(va_g3_ascan_real_tps[i].freq !=0x00)
        {
            ASCAN_PRINTF("%d,   %d, %s\n",va_g3_ascan_real_tps[i].print_freq,va_g3_ascan_real_tps[i].symbolrate,va_g3_ascan_real_tps[i].polarize==0x00?"H":"V");
        }
        else
            break;
    }
    ASCAN_PRINTF("Lost TPs:\n");
    for(i=0;i<VD_TP_CNT;i++)
    {
        if(va_g3_ascan_lost_tps[i].freq !=0x00)
        {
            ASCAN_PRINTF("%d,   %d, %s\n",va_g3_ascan_lost_tps[i].print_freq,va_g3_ascan_lost_tps[i].symbolrate,va_g3_ascan_lost_tps[i].polarize==0x00?"H":"V");
        }
        else
            break;
    }
    ASCAN_PRINTF("\nWarning TPs:\n");
    for(i=0;i<VD_TP_CNT;i++)
    {
        if(va_g3_ascan_war_tps[i].freq !=0x00)
        {
            ASCAN_PRINTF("%d,   %d, %s\n",va_g3_ascan_war_tps[i].print_freq,va_g3_ascan_war_tps[i].symbolrate,va_g3_ascan_war_tps[i].polarize==0x00?"H":"V");
        }
        else
            break;
    }
    ASCAN_PRINTF("\n");
    return SUCCESS;
}

static INT32 nim_s3501_ascan_tps_lock_check()
{
    int i;
    UINT32 b_time=osal_get_tick();
    UINT32 sel_idx=vs_s3501_ascan.va_ascan_g2_tplist_index;
    nim_s3501_ascan_convert_freq_proc(0x00,0x01);
    for(i=0;i<VD_TP_CNT;i++)
    {
        if(va_g2_ascan_tps[sel_idx][i].freq== 0x00)
            continue;
        if(!va_check_all)
        {
            if(va_g2_ascan_tps[sel_idx][i].ref_freq !=va_frq  ||va_g2_ascan_tps[sel_idx][i].polarize !=va_polar)
                continue;
        }
        tmp_lock=0x00;
        nim_s3501_channel_change(dev_get_by_id ( HLD_DEV_TYPE_NIM, 0), va_g2_ascan_tps[sel_idx][i].freq, va_g2_ascan_tps[sel_idx][i].symbolrate, 0);
        if(tmp_lock)
            va_g2_ascan_tps[sel_idx][i].lock_stat=0x01;
    }
    lck_time_cal +=osal_get_tick() - b_time;

    return SUCCESS;
}

static INT32 nim_s3501_ascan_tp_list_process()
{
    UINT32 i,j;
    UINT32 sel_idx=vs_s3501_ascan.va_ascan_g2_tplist_index;
    UINT32 t_real_idx=0x00,t_lost_idx=0x00,t_war_idx=0x00;
    UINT32 s_freq,r_freq;
    UINT8 t_find=0x00;

    for(i=0;i<VD_TP_CNT;i++)
    {
        s_freq=va_g2_ascan_tps[sel_idx][i].print_freq;
        if(s_freq ==0x00)
            break;

        t_find=0x00;
        j=0x00;
        while(va_g3_ascan_real_tps[j].print_freq !=0x00)
        {
            if(va_g3_ascan_real_tps[j].polarize != va_g2_ascan_tps[sel_idx][i].polarize)
            {
                j++;
                continue;
            }
            r_freq=va_g3_ascan_real_tps[j].print_freq;
            if(s_freq ==r_freq ||s_freq+1 ==r_freq || s_freq-1 == r_freq || s_freq+2 ==r_freq || s_freq-2 == r_freq )
            {

                t_find=0x01;
                break;
            }
            j++;
        }
        if(t_find)
            continue;
    check_tp:
        if(va_g2_ascan_tps[sel_idx][i].lock_stat)
        {
            nim_s3501_ascan_add_tp(ASCAN_ADD_LOST_TP,va_g2_ascan_tps[sel_idx][i].print_freq,s_freq,va_g2_ascan_tps[sel_idx][i].symbolrate,va_g2_ascan_tps[sel_idx][i].polarize);
        }
        else
        {
            nim_s3501_ascan_add_tp(ASCAN_ADD_WAR_TP,va_g2_ascan_tps[sel_idx][i].print_freq,s_freq,va_g2_ascan_tps[sel_idx][i].symbolrate,va_g2_ascan_tps[sel_idx][i].polarize);
        }

    }
    return SUCCESS;
}
INT32 nim_s3501_ascan_start_process()
{
    int i;
    vs_s3501_ascan.va_ascan_g1_real_begin_time=osal_get_tick();

    va_g3_ascan_real_tps_index=0x00;
    va_g3_ascan_lost_tps_index=0x00;
    va_g3_ascan_war_tps_index=0x00;
    memset(va_g3_ascan_real_tps,0,VD_TP_CNT*sizeof(struct nim_s3501_tp));
    memset(va_g3_ascan_lost_tps,0,VD_TP_CNT*sizeof(struct nim_s3501_tp));
    memset(va_g3_ascan_war_tps,0,VD_TP_CNT*sizeof(struct nim_s3501_tp));
    for(i=0;i<VD_TP_CNT;i++)
        va_g2_ascan_tps[vs_s3501_ascan.va_ascan_g2_tplist_index][i].lock_stat=0x00;
    lck_time_cal=0x00;
    va_polar=0x00;
    va_frq=0x00;
    va_check_all=0x00;
    return SUCCESS;
}

INT32 nim_s3501_ascan_end_process()
{
    UINT32 tmp_val=0x00;

    vs_s3501_ascan.va_ascan_g1_end_time=osal_get_tick();

    tmp_val=vs_s3501_ascan.va_ascan_g8_loop_cur_idx;
    if(tmp_val < VD_OSD_RPT_CNT)
    {
        vs_s3501_ascan.va_ascan_g1_time[tmp_val]=vs_s3501_ascan.va_ascan_g1_end_time-vs_s3501_ascan.va_ascan_g1_real_begin_time-lck_time_cal;
        vs_s3501_ascan.va_ascan_g3_real_tp_cnt[tmp_val]=va_g3_ascan_real_tps_index;
    }

    nim_s3501_ascan_tp_list_process();
    if(tmp_val < VD_OSD_RPT_CNT)
    {
        vs_s3501_ascan.va_ascan_g5_lost_tp_cnt[tmp_val]=va_g3_ascan_lost_tps_index;
        vs_s3501_ascan.va_ascan_g5_war_tp_cnt[tmp_val]=va_g3_ascan_war_tps_index;
    }
    nim_s3501_ascan_print();

    vs_s3501_ascan.va_ascan_g8_loop_cur_idx++;
    return SUCCESS;
}

static INT32 nim_s3501_ascan_add_tp(UINT32 type,UINT32 prt_freq,UINT32 freq, UINT32 sym, UINT32 polar)
{
    UINT32 temp;
    static UINT32 t_freq=0x00;
    static UINT32 t_pol=0x00;
    UINT32 t_freq1=0x00;
    switch(type)
    {
            case ASCAN_ADD_REAL_TP:
                temp=va_g3_ascan_real_tps_index;
                t_freq1=va_g3_ascan_real_tps[temp].print_freq=nim_s3501_ascan_convert_freq_proc(freq,0);
                va_g3_ascan_real_tps[temp].freq=freq;
                va_g3_ascan_real_tps[temp].symbolrate=sym;
                va_g3_ascan_real_tps[temp].polarize=va_polar;
                if((t_freq ==t_freq1 || t_freq==t_freq1-1 ||t_freq==t_freq1+1) && t_pol==va_polar )
                    break;
                va_g3_ascan_real_tps_index++;
                t_freq=va_g3_ascan_real_tps[temp].print_freq;
                t_pol=va_g3_ascan_real_tps[temp].polarize;
                break;
            case ASCAN_ADD_LOST_TP:
                temp=va_g3_ascan_lost_tps_index;
                va_g3_ascan_lost_tps[temp].print_freq=prt_freq;
                va_g3_ascan_lost_tps[temp].freq=freq;
                va_g3_ascan_lost_tps[temp].symbolrate=sym;
                va_g3_ascan_lost_tps[temp].polarize=polar;
                va_g3_ascan_lost_tps_index++;
                break;
            case ASCAN_ADD_WAR_TP:
                temp=va_g3_ascan_war_tps_index;
                va_g3_ascan_war_tps[temp].print_freq=prt_freq;
                va_g3_ascan_war_tps[temp].freq=freq;
                va_g3_ascan_war_tps[temp].symbolrate=sym;
                va_g3_ascan_war_tps[temp].polarize=polar;
                va_g3_ascan_war_tps_index++;
                break;
            default:
                break;
    }
    return SUCCESS;
}

static UINT32 nim_s3501_ascan_convert_freq_proc(UINT32 freq,UINT32 get_check)
{

    UINT32 frq;
    #if 1
    UINT32 lnb_high = ext_info->antenna.lnb_high;
    UINT32 lnb_low = ext_info->antenna.lnb_low;
    UINT32 lnb_ctrl = ext_info->antenna.lnb_type;
    UINT16 pol;

    if ((ext_info->dual_LNB)&&(lnb_ctrl!=LNB_CTRL_22K)&&(lnb_ctrl!=LNB_CTRL_UNICABLE)/*Unicable*/)
    {
        frq=(pol=(freq>1550))? lnb_high:lnb_low;
        if(get_check)
            va_check_all=0x01;
    }
    else
    {
        pol = ext_info->polar;
        if (lnb_ctrl != LNB_CTRL_22K && lnb_ctrl != LNB_CTRL_UNICABLE/*Unicable*/)
            frq = lnb_high>0? lnb_high:lnb_low;
        else
            frq = ((lnb_low<lnb_high)^(ext_info->antenna.k22))?lnb_low : lnb_high;
    }
    va_frq=frq;
    va_polar=pol;

    if (frq>KU_C_BAND_INTERFACE)
    {   /* Ku-band*/
        /* for Unicable, the Fub is inverted,
           so should autoscan from Hi freq to Low freq
           like C-band */
        if (lnb_ctrl == LNB_CTRL_UNICABLE)
            frq+=(ext_info->nim_para.sfreq+ext_info->nim_para.efreq-freq);
        else
            frq+=freq;
    }
    else/* C-band */
        frq-=freq;
    #endif

    return frq;

}
UINT32 nim_s3501_ascan_get_loop_cnt()
{
    return vs_s3501_ascan.va_ascan_g8_loop_cnt;
}
void nim_s3501_ascan_set_loop_cnt(UINT32 l_cnt)
{
    vs_s3501_ascan.va_ascan_g8_loop_cnt=l_cnt;
}

UINT32 nim_s3501_ascan_get_time(UINT32 idx)
{
    return vs_s3501_ascan.va_ascan_g1_time[idx];
}
UINT32 nim_s3501_ascan_get_tps(UINT32 idx)
{
    return vs_s3501_ascan.va_ascan_g3_real_tp_cnt[idx];
}
UINT32 nim_s3501_ascan_get_scan_tp_only()
{
    return vs_s3501_ascan.va_ascan_g7_scan_tp_only;
}
void nim_s3501_ascan_set_scan_tp_only(UINT32 t_only)
{
    vs_s3501_ascan.va_ascan_g7_scan_tp_only=t_only;
}
UINT32 nim_s3501_ascan_get_freq_start()
{
    return vs_s3501_ascan.va_ascan_g6_scan_f_start;
}
void nim_s3501_ascan_set_freq_start(UINT32 f_start)
{
    vs_s3501_ascan.va_ascan_g6_scan_f_start=f_start;
}
UINT32 nim_s3501_ascan_get_freq_end()
{
    return vs_s3501_ascan.va_ascan_g6_scan_f_end;
}
void nim_s3501_ascan_set_freq_end(UINT32 f_end)
{
    vs_s3501_ascan.va_ascan_g6_scan_f_end=f_end;
}
UINT32 nim_s3501_ascan_get_log_onoff()
{
    return vs_s3501_ascan.va_ascan_g4_open_log_print;
}
void nim_s3501_ascan_set_log_onoff(UINT32 on_off)
{
    vs_s3501_ascan.va_ascan_g4_open_log_print=on_off;
}
UINT32 nim_s3501_ascan_get_sats_idx()
{
    return vs_s3501_ascan.va_ascan_g2_tplist_index+1;
}
void nim_s3501_ascan_set_sats_idx(UINT32 sel_idx)
{
    vs_s3501_ascan.va_ascan_g2_tplist_index=sel_idx-1;
}
void nim_s3501_ascan_clear_loop_cur_idx()
{
    vs_s3501_ascan.va_ascan_g8_loop_cur_idx=0x00;
}

UINT32 nim_s3501_ascan_get_top_flag()
{
    return ascan_stop_flag;
}
void nim_s3501_ascan_set_info(struct as_module_info *a_info)
{
    ext_info=a_info;
}

#endif

